summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@redhat.com>2000-12-20 04:41:36 +0000
committerOwen Taylor <otaylor@src.gnome.org>2000-12-20 04:41:36 +0000
commit80a15829135065e16ce5b129c715d10d14b829ba (patch)
tree7b872c5a254ce857c44965e31ae07f2ded4ab26e
Since Xft may only be available statically without shlib deps, check forpango-start
Tue Dec 19 22:47:16 2000 Owen Taylor <otaylor@redhat.com> * configure.in pango-config.in pangoxft.pc.in modules/basic/Makefile.am: Since Xft may only be available statically without shlib deps, check for FreeType libs explicitly and include them when linking, otherwise things won't work. Also, define FREETYPE_CFLAGS from freetype-config --cflags. * modules/basic/basic-xft.c pango/pangoxft-font{,map}.c: Fool Xft into not converting glyph indices by loading the face unencoded then calling FT_Set_Charmap ourselves. * pango/Makefile.am pango/pango-ot.h pango/opentype/* :Add start of opentype handling - most of the actually meat of the code here is the OpenType layout code from FreeType 1 ported to freetype2 and adapted slighlty for our purposes. Also, includes a incomplete OpenType-table-dumping code useful for figuring out what is going on. * pango/pangoxft.h pango/pangoxft-font.h: Add calls for getting FT_Face and PangoOTInfo from PangoXftFont. * modules/arabic/{Makefile.am,arabic-ot.[ch],arabic-xft.c}: Initial support for rendering Arabic with OpenType fonts.
-rw-r--r--src/.cvsignore8
-rw-r--r--src/FT-license.txt158
-rw-r--r--src/Makefile.am39
-rw-r--r--src/README36
-rw-r--r--src/disasm.c317
-rw-r--r--src/disasm.h26
-rw-r--r--src/ftxgdef.c1155
-rw-r--r--src/ftxgdef.h220
-rw-r--r--src/ftxgpos.c6222
-rw-r--r--src/ftxgpos.h858
-rw-r--r--src/ftxgsub.c4531
-rw-r--r--src/ftxgsub.h612
-rw-r--r--src/ftxopen.c1467
-rw-r--r--src/ftxopen.h308
-rw-r--r--src/ftxopenf.h161
-rw-r--r--src/ottest.c265
-rw-r--r--src/pango-ot-info.c438
-rw-r--r--src/pango-ot-private.h98
-rw-r--r--src/pango-ot-ruleset.c232
19 files changed, 17151 insertions, 0 deletions
diff --git a/src/.cvsignore b/src/.cvsignore
new file mode 100644
index 00000000..5195f99d
--- /dev/null
+++ b/src/.cvsignore
@@ -0,0 +1,8 @@
+Makefile.in
+Makefile
+makefile.mingw
+ottest
+*.lo
+*.la
+.deps
+.libs
diff --git a/src/FT-license.txt b/src/FT-license.txt
new file mode 100644
index 00000000..8dae6094
--- /dev/null
+++ b/src/FT-license.txt
@@ -0,0 +1,158 @@
+ The FreeType Project LICENSE
+ ----------------------------
+
+ Copyright 1996-1999 by
+ David Turner, Robert Wilhelm, and Werner Lemberg
+
+
+
+Introduction
+============
+
+ The FreeType Project is distributed in several archive packages;
+ some of them may contain, in addition to the FreeType font engine,
+ various tools and contributions which rely on, or relate to, the
+ FreeType Project.
+
+ This license applies to all files found in such packages, and
+ which do not fall under their own explicit license. The license
+ affects thus the FreeType font engine, the test programs,
+ documentation and makefiles, at the very least.
+
+ This license was inspired by the BSD, Artistic, and IJG
+ (Independent JPEG Group) licenses, which all encourage inclusion
+ and use of free software in commercial and freeware products
+ alike. As a consequence, its main points are that:
+
+ o We don't promise that this software works. However, we are be
+ interested in any kind of bug reports. (`as is' distribution)
+
+ o You can use this software for whatever you want, in parts or
+ full form, without having to pay us. (`royalty-free' usage)
+
+ o You may not pretend that you wrote this software. If you use
+ it, or only parts of it, in a program, you must acknowledge
+ somewhere in your documentation that you've used the FreeType
+ code. (`credits')
+
+ We specifically permit and encourage the inclusion of this
+ software, with or without modifications, in commercial products,
+ provided that all warranty or liability claims are assumed by the
+ product vendor.
+
+
+Legal Terms
+===========
+
+0. Definitions
+--------------
+
+ Throughout this license, the terms `package', `FreeType Project',
+ and `FreeType archive' refer to the set of files originally
+ distributed by the authors (David Turner, Robert Wilhelm, and
+ Werner Lemberg) as the `FreeType project', be they named as alpha,
+ beta or final release.
+
+ `You' refers to the licensee, or person using the project, where
+ `using' is a generic term including compiling the project's source
+ code as well as linking it to form a `program' or `executable'.
+ This program is referred to as `a program using the FreeType
+ engine'.
+
+ This license applies to all files distributed in the original
+ FreeType archive, including all source code, binaries and
+ documentation, unless otherwise stated in the file in its
+ original, unmodified form as distributed in the original archive.
+ If you are unsure whether or not a particular file is covered by
+ this license, you must contact us to verify this.
+
+ The FreeType project is copyright (C) 1996-1999 by David Turner,
+ Robert Wilhelm, and Werner Lemberg. All rights reserved except as
+ specified below.
+
+1. No Warranty
+--------------
+
+ THE FREETYPE ARCHIVE IS PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OR THE INABILITY TO
+ USE, OF THE FREETYPE PROJECT.
+
+ As you have not signed this license, you are not required to
+ accept it. However, as the FreeType project is copyrighted
+ material, only this license, or another one contracted with the
+ authors, grants you the right to use, distribute, and modify it.
+ Therefore, by using, distributing, or modifying the FreeType
+ project, you indicate that you understand and accept all the terms
+ of this license.
+
+2. Redistribution
+-----------------
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ o Redistribution of source code must retain this license file
+ (`licence.txt') unaltered; any additions, deletions or changes
+ to the original files must be clearly indicated in
+ accompanying documentation. The copyright notices of the
+ unaltered, original files must be preserved in all copies of
+ source files.
+
+ o Redistribution in binary form must provide a disclaimer that
+ states that the software is based in part of the work of the
+ FreeType Team, in the distribution documentation. We also
+ encourage you to put an URL to the FreeType web page in your
+ documentation, though this isn't mandatory.
+
+ These conditions apply to any software derived from or based on
+ the FreeType code, not just the unmodified files. If you use our
+ work, you must acknowledge us. However, no fee need be paid to
+ us.
+
+3. Advertising
+--------------
+
+ The names of FreeType's authors and contributors may not be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+
+ We suggest, but do not require, that you use one or more of the
+ following phrases to refer to this software in your documentation
+ or advertising materials: `FreeType Project', `FreeType Engine',
+ `FreeType library', or `FreeType Distribution'.
+
+4. Contacts
+-----------
+
+ There are two mailing lists related to FreeType:
+
+ o freetype@freetype.org
+
+ Discusses general use and applications of FreeType, as well as
+ future and wanted additions to the library and distribution.
+ If you are looking for support, start in this list if you
+ haven't found anything to help you in the documentation.
+
+ o devel@freetype.org
+
+ Discusses bugs, as well as engine internals, design issues,
+ specific licenses, porting, etc.
+
+ o http://www.freetype.org
+
+ Holds the current FreeType web page, which will allow you to
+ download our latest development version and read online
+ documentation.
+
+ You can also contact us individually at:
+
+ David Turner <david.turner@freetype.org>
+ Robert Wilhelm <robert.wilhelm@freetype.org>
+ Werner Lemberg <werner.lemberg@freetype.org>
+
+
+--- end of license.txt ---
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 00000000..38cbfd23
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,39 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = \
+ -DSYSCONFDIR=\"$(sysconfdir)\" \
+ -DLIBDIR=\"$(libdir)\" \
+ $(FREETYPE_CFLAGS) \
+ $(X_CFLAGS) \
+ -I$(top_srcdir)
+
+LDADDS = @STRIP_BEGIN@ \
+ @x_ldflags@ \
+ @x_libs@ \
+ @GLIB_LIBS@ \
+ -lm \
+@STRIP_END@
+
+noinst_LTLIBRARIES = libpango-ot.la
+
+libpango_ot_la_SOURCES = \
+ ftxopen.c \
+ ftxgdef.c \
+ ftxgpos.c \
+ pango-ot-info.c \
+ pango-ot-ruleset.c \
+ ftxgsub.c
+
+noinst_PROGRAMS = ottest
+
+ottest_SOURCES = \
+ ottest.c \
+ disasm.c \
+ disasm.h
+
+ottest_LDADD = \
+ libpango-ot.la \
+ $(FREETYPE_LIBS)
+
+EXTRA_DIST = \
+ README
diff --git a/src/README b/src/README
new file mode 100644
index 00000000..6deb7f52
--- /dev/null
+++ b/src/README
@@ -0,0 +1,36 @@
+This directory includes code for using OpenType Layout tables from
+OpenType fonts with FreeType and
+
+The table reading code in:
+
+ ftxopen.[ch]
+ ftxopenf.h
+ ftxgdef.[ch]
+ ftxgpos.[ch]
+ ftxgdef.[ch]
+
+Is derived 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.)
+
+This code should be left following the FreeType indentation style and
+coding conventions.
+
+In addition to porting to FreeType-2, it has been modified to
+add support for PangoGlyphString's log_clusters, and in various
+other ways. Bug reports on these files should be sent to
+gtk-i18n-list@gtk.org, NOT to the freetype maintainers.
+
+The license for these files is in the file freetype-license.txt.
+
+
+Most of the additional files in this directory implement a high-level
+interface to this that follows Pango conventions and integrates with
+Pango.
+
+disasm.[ch] is a partial dumper for OpenType layout tables useful
+in figuring out what is going on. Please extend to cover additional
+parts of the tables as you encounter fonts using them.
+
+Owen Taylor
+17 December 2000 \ No newline at end of file
diff --git a/src/disasm.c b/src/disasm.c
new file mode 100644
index 00000000..2e064ec2
--- /dev/null
+++ b/src/disasm.c
@@ -0,0 +1,317 @@
+/* Pango
+ * disasm.c: Dump OpenType layout tables
+ *
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdarg.h>
+
+#include "disasm.h"
+
+#define DUMP(args...) dump (stream, indent, args)
+#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 ">%#4x</" #fld ">\n", (strct)->fld)
+
+#define DEF_DUMP(type) static void Dump_ ## type (TTO_ ## type *type, FILE *stream, int indent, FT_Bool is_gsub)
+#define RECURSE(name, type, val) do { DUMP ("<" #name ">\n"); Dump_ ## type (val, stream, indent + 1, is_gsub); DUMP ("</" #name ">\n"); } while (0)
+
+static void
+do_indent (FILE *stream, int indent)
+{
+ int i;
+
+ for (i = 0; i < indent * 3; i++)
+ fputc (' ', stream);
+}
+
+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
+Print_Tag (FT_ULong tag, FILE *stream)
+{
+ fprintf (stream, "%c%c%c%c",
+ (unsigned char)(tag >> 24),
+ (unsigned char)((tag & 0xff0000) >> 16),
+ (unsigned char)((tag & 0xff00) >> 8),
+ (unsigned char)(tag & 0xff));
+}
+
+DEF_DUMP (LangSys)
+{
+ int i;
+
+ DUMP_FUINT (LangSys, LookupOrderOffset);
+ DUMP_FUINT (LangSys, ReqFeatureIndex);
+ DUMP_FUINT (LangSys, FeatureCount);
+
+ for (i=0; i < LangSys->FeatureCount; i++)
+ DUMP("<FeatureIndex>%d</FeatureIndex>\n", LangSys->FeatureIndex[i]);
+}
+
+DEF_DUMP (Script)
+{
+ int i;
+
+ RECURSE (DefaultLangSys, LangSys, &Script->DefaultLangSys);
+
+ DUMP_FUINT (Script, LangSysCount);
+
+ for (i=0; i < Script->LangSysCount; i++)
+ {
+ do_indent (stream, indent);
+ fprintf (stream, "<LangSysTag>");
+ Print_Tag (Script->LangSysRecord[i].LangSysTag, stream);
+ fprintf (stream, "</LangSysTag>\n");
+ RECURSE (LangSys, LangSys, &Script->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 (Script, Script, &ScriptList->ScriptRecord[i].Script);
+ }
+}
+
+DEF_DUMP (Feature)
+{
+ int i;
+
+ DUMP_FUINT (Feature, FeatureParams);
+ DUMP_FUINT (Feature, LookupListCount);
+
+ for (i=0; i < Feature->LookupListCount; i++)
+ DUMP("<LookupIndex>%d</LookupIndex>\n", Feature->LookupListIndex[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>\n");
+ RECURSE (Feature, Feature, &FeatureList->FeatureRecord[i].Feature);
+ }
+}
+
+DEF_DUMP (Coverage)
+{
+ 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++)
+ DUMP("<Glyph>%#4x</Glyph> <!-- %d -->\n", Coverage->cf.cf1.GlyphArray[i], i);
+ }
+ else
+ {
+ }
+}
+
+static void
+Dump_GSUB_Lookup_Single (TTO_SubTable *subtable, FILE *stream, int indent, FT_Bool is_gsub)
+{
+ TTO_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++)
+ DUMP("<Substitute>%#4x</Substitute> <!-- %d -->\n", SingleSubst->ssf.ssf2.Substitute[i], i);
+ }
+}
+
+DEF_DUMP (Ligature)
+{
+ int i;
+
+ DUMP_FGLYPH (Ligature, LigGlyph);
+ DUMP_FUINT (Ligature, ComponentCount);
+
+ for (i=0; i < Ligature->ComponentCount - 1; i++)
+ DUMP("<Component>%#4x</Component>\n", Ligature->Component[i]);
+}
+
+DEF_DUMP (LigatureSet)
+{
+ int i;
+
+ DUMP_FUINT (LigatureSet, LigatureCount);
+
+ for (i=0; i < LigatureSet->LigatureCount; i++)
+ RECURSE (Ligature, Ligature, &LigatureSet->Ligature[i]);
+}
+
+static void
+Dump_GSUB_Lookup_Ligature (TTO_SubTable *subtable, FILE *stream, int indent, FT_Bool is_gsub)
+{
+ int i;
+ TTO_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 (LigatureSet, LigatureSet, &LigatureSubst->LigatureSet[i]);
+}
+
+DEF_DUMP (Lookup)
+{
+ int i;
+ const char *lookup_name = NULL;
+ void (*lookup_func) (TTO_SubTable *subtable, FILE *stream, int indent, FT_Bool is_gsub) = NULL;
+
+ if (is_gsub)
+ {
+ switch (Lookup->LookupType)
+ {
+ case GSUB_LOOKUP_SINGLE:
+ lookup_name = "SINGLE";
+ lookup_func = Dump_GSUB_Lookup_Single;
+ break;
+ case GSUB_LOOKUP_MULTIPLE:
+ lookup_name = "MULTIPLE";
+ break;
+ case GSUB_LOOKUP_ALTERNATE:
+ lookup_name = "ALTERNATE";
+ break;
+ case GSUB_LOOKUP_LIGATURE:
+ lookup_name = "LIGATURE";
+ lookup_func = Dump_GSUB_Lookup_Ligature;
+ break;
+ case GSUB_LOOKUP_CONTEXT:
+ lookup_name = "CONTEXT";
+ break;
+ case GSUB_LOOKUP_CHAIN:
+ lookup_name = "CHAIN";
+ break;
+ }
+ }
+ else
+ {
+ switch (Lookup->LookupType)
+ {
+ case GPOS_LOOKUP_SINGLE:
+ lookup_name = "SINGLE";
+ break;
+ case GPOS_LOOKUP_PAIR:
+ lookup_name = "PAIR";
+ break;
+ case GPOS_LOOKUP_CURSIVE:
+ lookup_name = "CURSIVE";
+ break;
+ case GPOS_LOOKUP_MARKBASE:
+ lookup_name = "MARKBASE";
+ break;
+ case GPOS_LOOKUP_MARKLIG:
+ lookup_name = "MARKLIG";
+ break;
+ case GPOS_LOOKUP_MARKMARK:
+ lookup_name = "MARKMARK";
+ break;
+ case GPOS_LOOKUP_CONTEXT:
+ lookup_name = "CONTEXT";
+ break;
+ case GPOS_LOOKUP_CHAIN:
+ lookup_name = "CHAIN";
+ break;
+ }
+ }
+
+ DUMP("<LookupType>%s</LookupType>\n", lookup_name);
+
+ for (i=0; i < Lookup->SubTableCount; i++)
+ {
+ DUMP ("<Subtable>\n");
+ if (lookup_func)
+ (*lookup_func) (&Lookup->SubTable[i], stream, indent + 1, is_gsub);
+ DUMP ("</Subtable>\n");
+ }
+}
+
+DEF_DUMP (LookupList)
+{
+ int i;
+
+ DUMP_FUINT (LookupList, LookupCount);
+
+ for (i=0; i < LookupList->LookupCount; i++)
+ RECURSE (Lookup, Lookup, &LookupList->Lookup[i]);
+}
+
+void
+TT_Dump_GSUB_Table (TTO_GSUB gsub, FILE *stream)
+{
+ int indent = 0;
+ FT_Bool is_gsub = 1;
+
+ RECURSE (ScriptList, ScriptList, &gsub->ScriptList);
+ RECURSE (FeatureList, FeatureList, &gsub->FeatureList);
+ RECURSE (LookupList, LookupList, &gsub->LookupList);
+}
+
+void
+TT_Dump_GPOS_Table (TTO_GPOS gpos, FILE *stream)
+{
+ int indent = 0;
+ FT_Bool is_gsub = 0;
+
+ RECURSE (ScriptList, ScriptList, &gpos->ScriptList);
+ RECURSE (FeatureList, FeatureList, &gpos->FeatureList);
+ RECURSE (LookupList, LookupList, &gpos->LookupList);
+}
diff --git a/src/disasm.h b/src/disasm.h
new file mode 100644
index 00000000..e7556d40
--- /dev/null
+++ b/src/disasm.h
@@ -0,0 +1,26 @@
+/* Pango
+ * disasm.h: Dump OpenType layout tables
+ *
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include "ftxopen.h"
+
+void TT_Dump_GSUB_Table (TTO_GSUB gsub, FILE *stream);
+void TT_Dump_GPOS_Table (TTO_GPOS gpos, FILE *stream);
diff --git a/src/ftxgdef.c b/src/ftxgdef.c
new file mode 100644
index 00000000..dd1754c6
--- /dev/null
+++ b/src/ftxgdef.c
@@ -0,0 +1,1155 @@
+/*******************************************************************
+ *
+ * ftxgdef.c
+ *
+ * TrueType Open GDEF table support.
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ ******************************************************************/
+
+#include <freetype/tttags.h>
+
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/ftmemory.h>
+#include <freetype/internal/tterrors.h>
+#include <freetype/internal/tttypes.h>
+
+#include "ftxopen.h"
+#include "ftxopenf.h"
+
+#define TTAG_GDEF FT_MAKE_TAG( 'G', 'D', 'E', 'F' )
+
+ static FT_Error Load_AttachList( TTO_AttachList* al,
+ FT_Stream stream );
+ static FT_Error Load_LigCaretList( TTO_LigCaretList* lcl,
+ FT_Stream stream );
+
+ static void Free_AttachList( TTO_AttachList* al,
+ FT_Memory memory );
+ static void Free_LigCaretList( TTO_LigCaretList* lcl,
+ FT_Memory memory );
+
+ static void Free_NewGlyphClasses( TTO_GDEFHeader* gdef,
+ FT_Memory memory );
+
+
+
+ /**********************
+ * Extension Functions
+ **********************/
+
+#if 0
+#define GDEF_ID Build_Extension_ID( 'G', 'D', 'E', 'F' )
+
+
+ static FT_Error GDEF_Create( void* ext,
+ PFace face )
+ {
+ DEFINE_LOAD_LOCALS( face->stream );
+
+ TTO_GDEFHeader* gdef = (TTO_GDEFHeader*)ext;
+ Long table;
+
+
+ /* by convention */
+
+ if ( !gdef )
+ return TT_Err_Ok;
+
+ /* a null offset indicates that there is no GDEF table */
+
+ gdef->offset = 0;
+
+ /* we store the start offset and the size of the subtable */
+
+ table = TT_LookUp_Table( face, TTAG_GDEF );
+ if ( table < 0 )
+ return TT_Err_Ok; /* The table is optional */
+
+ if ( FILE_Seek( face->dirTables[table].Offset ) ||
+ ACCESS_Frame( 4L ) )
+ return error;
+
+ gdef->offset = FILE_Pos() - 4L; /* undo ACCESS_Frame() */
+ gdef->Version = GET_ULong();
+
+ FORGET_Frame();
+
+ gdef->loaded = FALSE;
+
+ return TT_Err_Ok;
+ }
+
+
+ static FT_Error GDEF_Destroy( void* ext,
+ PFace face )
+ {
+ TTO_GDEFHeader* gdef = (TTO_GDEFHeader*)ext;
+
+
+ /* by convention */
+
+ if ( !gdef )
+ return TT_Err_Ok;
+
+ if ( gdef->loaded )
+ {
+ Free_LigCaretList( &gdef->LigCaretList, memory );
+ Free_AttachList( &gdef->AttachList, memory );
+ Free_ClassDefinition( &gdef->GlyphClassDef, memory );
+ Free_ClassDefinition( &gdef->MarkAttachClassDef, memory );
+
+ Free_NewGlyphClasses( gdef, memory );
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_Init_GDEF_Extension( TT_Engine engine )
+ {
+ PEngine_Instance _engine = HANDLE_Engine( engine );
+
+
+ if ( !_engine )
+ return TT_Err_Invalid_Engine;
+
+ return TT_Register_Extension( _engine,
+ GDEF_ID,
+ sizeof ( TTO_GDEFHeader ),
+ GDEF_Create,
+ GDEF_Destroy );
+ }
+#endif
+
+ EXPORT_FUNC
+ FT_Error TT_Load_GDEF_Table( FT_Face face,
+ TTO_GDEFHeader** retptr )
+ {
+ FT_Error error;
+ FT_Memory memory = face->memory;
+ FT_Stream stream = face->stream;
+ TT_Face tt_face = (TT_Face)face;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_GDEFHeader* gdef;
+
+
+ if ( !retptr )
+ return TT_Err_Invalid_Argument;
+
+ if (( error = tt_face->goto_table( tt_face, TTAG_GDEF, stream, 0 ) ))
+ return error;
+
+ if ( ALLOC( gdef, sizeof( *gdef ) ) )
+ return error;
+
+ gdef->memory = face->memory;
+
+ 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 = Load_ClassDefinition( &gdef->GlyphClassDef, 5,
+ stream ) ) != TT_Err_Ok )
+ goto Fail0;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ gdef->GlyphClassDef.loaded = FALSE;
+
+ 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_AttachList( &gdef->AttachList,
+ stream ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ gdef->AttachList.loaded = FALSE;
+
+ 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_LigCaretList( &gdef->LigCaretList,
+ stream ) ) != TT_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ gdef->LigCaretList.loaded = FALSE;
+
+ /* 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 current file offset. */
+
+ gdef->MarkAttachClassDef_offset = FILE_Pos();
+ gdef->MarkAttachClassDef.loaded = FALSE;
+
+ gdef->LastGlyph = 0;
+ gdef->NewGlyphClasses = NULL;
+
+ *retptr = gdef;
+ DONE_Stream( stream );
+
+ return TT_Err_Ok;
+
+ Fail2:
+ Free_AttachList( &gdef->AttachList, memory );
+
+ Fail1:
+ Free_ClassDefinition( &gdef->GlyphClassDef, memory );
+
+ Fail0:
+ FREE( gdef );
+
+ return error;
+ }
+
+ EXPORT_FUNC
+ FT_Error TT_Done_GDEF_Table ( TTO_GDEFHeader* gdef )
+ {
+ FT_Memory memory = gdef->memory;
+
+ Free_LigCaretList( &gdef->LigCaretList, memory );
+ Free_AttachList( &gdef->AttachList, memory );
+ Free_ClassDefinition( &gdef->GlyphClassDef, memory );
+ Free_ClassDefinition( &gdef->MarkAttachClassDef, memory );
+
+ Free_NewGlyphClasses( gdef, memory );
+
+ return TT_Err_Ok;
+ }
+
+
+
+
+ /*******************************
+ * AttachList related functions
+ *******************************/
+
+
+ /* AttachPoint */
+
+ static FT_Error Load_AttachPoint( TTO_AttachPoint* ap,
+ FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+
+ FT_UShort n, count;
+ FT_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, FT_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 TT_Err_Ok;
+ }
+
+
+ static void Free_AttachPoint( TTO_AttachPoint* ap,
+ FT_Memory memory )
+ {
+ FREE( ap->PointIndex );
+ }
+
+
+ /* AttachList */
+
+ static FT_Error Load_AttachList( TTO_AttachList* al,
+ FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &al->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ al->loaded = TRUE;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_AttachPoint( &ap[n], memory );
+
+ FREE( ap );
+
+ Fail2:
+ Free_Coverage( &al->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_AttachList( TTO_AttachList* al,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_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], memory );
+
+ FREE( ap );
+ }
+
+ Free_Coverage( &al->Coverage, memory );
+ }
+
+
+
+ /*********************************
+ * LigCaretList related functions
+ *********************************/
+
+
+ /* CaretValueFormat1 */
+ /* CaretValueFormat2 */
+ /* CaretValueFormat3 */
+ /* CaretValueFormat4 */
+
+ static FT_Error Load_CaretValue( TTO_CaretValue* cv,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+ FT_ULong 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 = Load_Device( &cv->cvf.cvf3.Device,
+ stream ) ) != TT_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 TTO_Err_Invalid_GDEF_SubTable_Format;
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ static void Free_CaretValue( TTO_CaretValue* cv,
+ FT_Memory memory )
+ {
+ if ( cv->CaretValueFormat == 3 )
+ Free_Device( &cv->cvf.cvf3.Device, memory );
+ }
+
+
+ /* LigGlyph */
+
+ static FT_Error Load_LigGlyph( TTO_LigGlyph* lg,
+ FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_CaretValue( &cv[n], memory );
+
+ FREE( cv );
+ return error;
+ }
+
+
+ static void Free_LigGlyph( TTO_LigGlyph* lg,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_CaretValue* cv;
+
+
+ if ( lg->CaretValue )
+ {
+ count = lg->CaretCount;
+ cv = lg->CaretValue;
+
+ for ( n = 0; n < count; n++ )
+ Free_CaretValue( &cv[n], memory );
+
+ FREE( cv );
+ }
+ }
+
+
+ /* LigCaretList */
+
+ static FT_Error Load_LigCaretList( TTO_LigCaretList* lcl,
+ FT_Stream stream )
+ {
+ FT_Memory memory = stream->memory;
+ FT_Error error;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &lcl->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ lcl->loaded = TRUE;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_LigGlyph( &lg[n], memory );
+
+ FREE( lg );
+
+ Fail2:
+ Free_Coverage( &lcl->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_LigCaretList( TTO_LigCaretList* lcl,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_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], memory );
+
+ FREE( lg );
+ }
+
+ Free_Coverage( &lcl->Coverage, memory );
+ }
+
+
+
+ /***********
+ * GDEF API
+ ***********/
+
+
+ static FT_UShort Get_New_Class( TTO_GDEFHeader* gdef,
+ FT_UShort glyphID,
+ FT_UShort index )
+ {
+ FT_UShort glyph_index, array_index;
+ FT_UShort byte, bits;
+
+ TTO_ClassRangeRecord* gcrr;
+ FT_UShort** ngc;
+
+
+ if ( glyphID >= gdef->LastGlyph )
+ return 0;
+
+ gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
+ ngc = gdef->NewGlyphClasses;
+
+ if ( glyphID < gcrr[index].Start )
+ {
+ array_index = 0;
+ 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 + 1];
+ bits = byte >> ( 16 - ( glyph_index % 4 + 1 ) * 4 );
+
+ return bits & 0x000F;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GDEF_Get_Glyph_Property( TTO_GDEFHeader* gdef,
+ FT_UShort glyphID,
+ FT_UShort* property )
+ {
+ FT_UShort class, index;
+
+ FT_Error error;
+
+
+ if ( !gdef || !property )
+ return TT_Err_Invalid_Argument;
+
+ /* first, we check for mark attach classes */
+
+ if ( gdef->MarkAttachClassDef.loaded )
+ {
+ error = Get_Class( &gdef->MarkAttachClassDef, glyphID, &class, &index );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ if ( !error )
+ {
+ *property = class << 8;
+ return TT_Err_Ok;
+ }
+ }
+
+ error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ /* if we have a constructed class table, check whether additional
+ values have been assigned */
+
+ if ( error == TTO_Err_Not_Covered && gdef->NewGlyphClasses )
+ class = Get_New_Class( gdef, glyphID, index );
+
+ switch ( class )
+ {
+ case UNCLASSIFIED_GLYPH:
+ *property = 0;
+ break;
+
+ case SIMPLE_GLYPH:
+ *property = TTO_BASE_GLYPH;
+ break;
+
+ case LIGATURE_GLYPH:
+ *property = TTO_LIGATURE;
+ break;
+
+ case MARK_GLYPH:
+ *property = TTO_MARK;
+ break;
+
+ case COMPONENT_GLYPH:
+ *property = TTO_COMPONENT;
+ break;
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ static FT_Error Make_ClassRange( TTO_ClassDefinition* cd,
+ FT_UShort start,
+ FT_UShort end,
+ FT_UShort class,
+ FT_Memory memory )
+ {
+ FT_Error error;
+ FT_UShort index;
+
+ TTO_ClassDefFormat2* cdf2;
+ TTO_ClassRangeRecord* crr;
+
+
+ cdf2 = &cd->cd.cd2;
+
+ if ( REALLOC_ARRAY( cdf2->ClassRangeRecord,
+ cdf2->ClassRangeCount,
+ cdf2->ClassRangeCount + 1 ,
+ TTO_ClassRangeRecord ) )
+ return error;
+
+ cdf2->ClassRangeCount++;
+
+ crr = cdf2->ClassRangeRecord;
+ index = cdf2->ClassRangeCount - 1;
+
+ crr[index].Start = start;
+ crr[index].End = end;
+ crr[index].Class = class;
+
+ cd->Defined[class] = TRUE;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GDEF_Build_ClassDefinition( TTO_GDEFHeader* gdef,
+ FT_UShort num_glyphs,
+ FT_UShort glyph_count,
+ FT_UShort* glyph_array,
+ FT_UShort* class_array )
+ {
+ FT_UShort start, curr_glyph, curr_class;
+ FT_UShort n, count;
+ FT_Error error;
+ FT_Memory memory = gdef->memory;
+
+ TTO_ClassDefinition* gcd;
+ TTO_ClassRangeRecord* gcrr;
+ FT_UShort** ngc;
+
+
+ if ( !gdef || !glyph_array || !class_array )
+ return TT_Err_Invalid_Argument;
+
+ gcd = &gdef->GlyphClassDef;
+
+ /* We build a format 2 table */
+
+ gcd->ClassFormat = 2;
+
+ /* A GlyphClassDef table contains at most 5 different class values */
+
+ if ( ALLOC_ARRAY( gcd->Defined, 5, FT_Bool ) )
+ return error;
+
+ 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 = TT_Err_Invalid_Argument;
+ goto Fail4;
+ }
+
+ glyph_count--;
+
+ for ( n = 0; n <= glyph_count; 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,
+ memory ) ) != TT_Err_Ok )
+ goto Fail3;
+ }
+ else
+ {
+ if ( curr_glyph == 0xFFFF )
+ {
+ error = TT_Err_Invalid_Argument;
+ goto Fail3;
+ }
+ else
+ curr_glyph++;
+ }
+ }
+ else
+ {
+ if ( ( error = Make_ClassRange( gcd, start,
+ curr_glyph - 1,
+ curr_class,
+ memory ) ) != TT_Err_Ok )
+ goto Fail3;
+
+ if ( curr_glyph > glyph_array[n] )
+ {
+ error = TT_Err_Invalid_Argument;
+ goto Fail3;
+ }
+
+ start = glyph_array[n];
+ curr_class = class_array[n];
+ curr_glyph = start;
+
+ if ( curr_class >= 5 )
+ {
+ error = TT_Err_Invalid_Argument;
+ goto Fail3;
+ }
+
+ if ( n == glyph_count )
+ {
+ if ( ( error = Make_ClassRange( gcd, start,
+ curr_glyph,
+ curr_class,
+ memory ) ) != TT_Err_Ok )
+ goto Fail3;
+ }
+ else
+ {
+ if ( curr_glyph == 0xFFFF )
+ {
+ error = TT_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, FT_UShort* ) )
+ goto Fail2;
+
+ 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 ( gcrr[0].Start )
+ {
+ if ( ALLOC_ARRAY( ngc[0], gcrr[0].Start / 4 + 1, FT_UShort ) )
+ goto Fail1;
+ }
+
+ 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 - 1 ) / 4 + 1,
+ FT_UShort ) )
+ goto Fail1;
+ }
+
+ if ( gcrr[count - 1].End != num_glyphs - 1 )
+ {
+ if ( ALLOC_ARRAY( ngc[count],
+ ( num_glyphs - gcrr[count - 1].End - 1 ) / 4 + 1,
+ FT_UShort ) )
+ goto Fail1;
+ }
+
+ gdef->LastGlyph = num_glyphs - 1;
+
+ gdef->MarkAttachClassDef_offset = 0L;
+ gdef->MarkAttachClassDef.loaded = FALSE;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ FREE( ngc[n] );
+
+ Fail2:
+ FREE( gdef->NewGlyphClasses );
+
+ Fail3:
+ FREE( gcd->cd.cd2.ClassRangeRecord );
+
+ Fail4:
+ FREE( gcd->Defined );
+ return error;
+ }
+
+
+ static void Free_NewGlyphClasses( TTO_GDEFHeader* gdef,
+ FT_Memory memory )
+ {
+ FT_UShort** ngc;
+ FT_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 );
+ }
+ }
+
+
+ FT_Error Add_Glyph_Property( TTO_GDEFHeader* gdef,
+ FT_UShort glyphID,
+ FT_UShort property )
+ {
+ FT_Error error;
+ FT_UShort class, new_class, index;
+ FT_UShort byte, bits, mask;
+ FT_UShort array_index, glyph_index;
+
+ TTO_ClassRangeRecord* gcrr;
+ FT_UShort** ngc;
+
+
+ error = Get_Class( &gdef->GlyphClassDef, glyphID, &class, &index );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ /* we don't accept glyphs covered in `GlyphClassDef' */
+
+ if ( !error )
+ return TTO_Err_Not_Covered;
+
+ switch ( property )
+ {
+ case 0:
+ new_class = UNCLASSIFIED_GLYPH;
+ break;
+
+ case TTO_BASE_GLYPH:
+ new_class = SIMPLE_GLYPH;
+ break;
+
+ case TTO_LIGATURE:
+ new_class = LIGATURE_GLYPH;
+ break;
+
+ case TTO_MARK:
+ new_class = MARK_GLYPH;
+ break;
+
+ case TTO_COMPONENT:
+ new_class = COMPONENT_GLYPH;
+ break;
+
+ default:
+ return TT_Err_Invalid_Argument;
+ }
+
+ gcrr = gdef->GlyphClassDef.cd.cd2.ClassRangeRecord;
+ ngc = gdef->NewGlyphClasses;
+
+ if ( glyphID < gcrr[index].Start )
+ {
+ array_index = 0;
+ 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 + 1];
+ 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 + 1] &= mask;
+ ngc[array_index][glyph_index / 4 + 1] |= bits;
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ FT_Error Check_Property( TTO_GDEFHeader* gdef,
+ FT_UShort index,
+ FT_UShort flags,
+ FT_UShort* property )
+ {
+ FT_Error error;
+
+
+ if ( gdef )
+ {
+ error = TT_GDEF_Get_Glyph_Property( gdef, index, property );
+ if ( error )
+ return error;
+
+ /* This is OpenType 1.2 */
+
+ if ( flags & IGNORE_SPECIAL_MARKS )
+ if ( (flags & 0xFF00) != *property )
+ return TTO_Err_Not_Covered;
+
+ if ( flags & *property )
+ return TTO_Err_Not_Covered;
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+/* END */
diff --git a/src/ftxgdef.h b/src/ftxgdef.h
new file mode 100644
index 00000000..6395bf38
--- /dev/null
+++ b/src/ftxgdef.h
@@ -0,0 +1,220 @@
+/*******************************************************************
+ *
+ * ftxgdef.h
+ *
+ * TrueType Open GDEF table support
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ ******************************************************************/
+
+#ifndef FTXOPEN_H
+#error "Don't include this file! Use ftxopen.h instead."
+#endif
+
+#ifndef FTXGDEF_H
+#define FTXGDEF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TTO_Err_Invalid_GDEF_SubTable_Format 0x1030
+#define TTO_Err_Invalid_GDEF_SubTable 0x1031
+
+
+/* GDEF glyph classes */
+
+#define UNCLASSIFIED_GLYPH 0
+#define SIMPLE_GLYPH 1
+#define LIGATURE_GLYPH 2
+#define MARK_GLYPH 3
+#define COMPONENT_GLYPH 4
+
+/* GDEF glyph properties, corresponding to class values 1-4. Note that
+ TTO_COMPONENT has no corresponding flag in the LookupFlag field. */
+
+#define TTO_BASE_GLYPH 0x0002
+#define TTO_LIGATURE 0x0004
+#define TTO_MARK 0x0008
+#define TTO_COMPONENT 0x0010
+
+
+ /* Attachment related structures */
+
+ struct TTO_AttachPoint_
+ {
+ FT_UShort PointCount; /* size of the PointIndex array */
+ FT_UShort* PointIndex; /* array of contour points */
+ };
+
+ typedef struct TTO_AttachPoint_ TTO_AttachPoint;
+
+
+ struct TTO_AttachList_
+ {
+ FT_Bool loaded;
+
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort GlyphCount; /* number of glyphs with
+ attachments */
+ TTO_AttachPoint* AttachPoint; /* array of AttachPoint tables */
+ };
+
+ typedef struct TTO_AttachList_ TTO_AttachList;
+
+
+ /* Ligature Caret related structures */
+
+ struct TTO_CaretValueFormat1_
+ {
+ FT_Short Coordinate; /* x or y value (in design units) */
+ };
+
+ typedef struct TTO_CaretValueFormat1_ TTO_CaretValueFormat1;
+
+
+ struct TTO_CaretValueFormat2_
+ {
+ FT_UShort CaretValuePoint; /* contour point index on glyph */
+ };
+
+ typedef struct TTO_CaretValueFormat2_ TTO_CaretValueFormat2;
+
+
+ struct TTO_CaretValueFormat3_
+ {
+ FT_Short Coordinate; /* x or y value (in design units) */
+ TTO_Device Device; /* Device table for x or y value */
+ };
+
+ typedef struct TTO_CaretValueFormat3_ TTO_CaretValueFormat3;
+
+
+ struct TTO_CaretValueFormat4_
+ {
+ FT_UShort IdCaretValue; /* metric ID */
+ };
+
+ typedef struct TTO_CaretValueFormat4_ TTO_CaretValueFormat4;
+
+
+ struct TTO_CaretValue_
+ {
+ FT_UShort CaretValueFormat; /* 1, 2, 3, or 4 */
+
+ union
+ {
+ TTO_CaretValueFormat1 cvf1;
+ TTO_CaretValueFormat2 cvf2;
+ TTO_CaretValueFormat3 cvf3;
+ TTO_CaretValueFormat4 cvf4;
+ } cvf;
+ };
+
+ typedef struct TTO_CaretValue_ TTO_CaretValue;
+
+
+ struct TTO_LigGlyph_
+ {
+ FT_Bool loaded;
+
+ FT_UShort CaretCount; /* number of caret values */
+ TTO_CaretValue* CaretValue; /* array of caret values */
+ };
+
+ typedef struct TTO_LigGlyph_ TTO_LigGlyph;
+
+
+ struct TTO_LigCaretList_
+ {
+ FT_Bool loaded;
+
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort LigGlyphCount; /* number of ligature glyphs */
+ TTO_LigGlyph* LigGlyph; /* array of LigGlyph tables */
+ };
+
+ typedef struct TTO_LigCaretList_ TTO_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 TTO_GDEFHeader_
+ {
+ FT_Memory memory;
+ FT_ULong offset;
+
+ FT_Fixed Version;
+
+ TTO_ClassDefinition GlyphClassDef;
+ TTO_AttachList AttachList;
+ TTO_LigCaretList LigCaretList;
+ FT_ULong MarkAttachClassDef_offset;
+ TTO_ClassDefinition MarkAttachClassDef; /* new in OT 1.2 */
+
+ FT_UShort LastGlyph;
+ FT_UShort** NewGlyphClasses;
+ };
+
+ typedef struct TTO_GDEFHeader_ TTO_GDEFHeader;
+ typedef struct TTO_GDEFHeader_* TTO_GDEF;
+
+
+ /* finally, the GDEF API */
+
+ /* EXPORT_DEF
+ FT_Error TT_Init_GDEF_Extension( TT_Engine engine ); */
+
+ EXPORT_DEF
+ FT_Error TT_Load_GDEF_Table( FT_Face face,
+ TTO_GDEFHeader** gdef );
+
+ EXPORT_DEF
+ FT_Error TT_Done_GDEF_Table ( TTO_GDEFHeader* gdef );
+
+ EXPORT_DEF
+ FT_Error TT_GDEF_Get_Glyph_Property( TTO_GDEFHeader* gdef,
+ FT_UShort glyphID,
+ FT_UShort* property );
+ EXPORT_DEF
+ FT_Error TT_GDEF_Build_ClassDefinition( TTO_GDEFHeader* gdef,
+ FT_UShort num_glyphs,
+ FT_UShort glyph_count,
+ FT_UShort* glyph_array,
+ FT_UShort* class_array );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FTXGDEF_H */
+
+
+/* END */
diff --git a/src/ftxgpos.c b/src/ftxgpos.c
new file mode 100644
index 00000000..48d50099
--- /dev/null
+++ b/src/ftxgpos.c
@@ -0,0 +1,6222 @@
+/*******************************************************************
+ *
+ * ftxgpos.c
+ *
+ * TrueType Open GPOS table support.
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ ******************************************************************/
+
+/* XXX There is *a lot* of duplicated code (cf. formats 7 and 8), but
+ I don't care currently. I believe that it would be possible to
+ save about 50% of TTO code by carefully designing the structures,
+ sharing as much as possible with extensive use of macros. This
+ is something for a volunteer :-) */
+
+#define TTAG_GPOS FT_MAKE_TAG( 'G', 'P', 'O', 'S' )
+
+#include <freetype/tttags.h>
+
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/ftmemory.h>
+#include <freetype/internal/tterrors.h>
+#include <freetype/internal/tttypes.h>
+
+#include "ftxopen.h"
+#include "ftxopenf.h"
+
+
+ struct GPOS_Instance_
+ {
+ TTO_GPOSHeader* gpos;
+ FT_Face face;
+ FT_Bool dvi;
+ FT_UShort load_flags; /* how the glyph should be loaded */
+ FT_Bool r2l;
+
+ FT_UShort last; /* the last valid glyph -- used
+ with cursive positioning */
+ FT_Pos anchor_x; /* the coordinates of the anchor point */
+ FT_Pos anchor_y; /* of the last valid glyph */
+ };
+
+ typedef struct GPOS_Instance_ GPOS_Instance;
+
+
+ static FT_Error Do_Glyph_Lookup( GPOS_Instance* gpi,
+ FT_UShort lookup_index,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_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 FT_Error default_mmfunc( FT_Face face,
+ FT_UShort metric_id,
+ FT_Pos* metric_value,
+ void* data )
+ {
+ return TTO_Err_No_MM_Interpreter;
+ }
+
+
+#if 0
+#define GPOS_ID Build_Extension_ID( 'G', 'P', 'O', 'S' )
+
+ /**********************
+ * Extension Functions
+ **********************/
+
+ static FT_Error GPOS_Create( void* ext,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ TTO_GPOSHeader* gpos = (TTO_GPOSHeader*)ext;
+ FT_Long table;
+
+
+ /* by convention */
+
+ if ( !gpos )
+ return TT_Err_Ok;
+
+ /* a null offset indicates that there is no GPOS table */
+
+ gpos->offset = 0;
+
+ /* we store the start offset and the size of the subtable */
+
+ table = face->lookup_table ( face, TTAG_GPOS );
+ if ( table < 0 )
+ return TT_Err_Ok; /* The table is optional */
+
+ if ( FILE_Seek( face->dirTables[table].Offset ) ||
+ ACCESS_Frame( 4L ) )
+ return error;
+
+ gpos->offset = FILE_Pos() - 4L; /* undo ACCESS_Frame() */
+ gpos->Version = GET_ULong();
+
+ FORGET_Frame();
+
+ /* a default mmfunc() handler which just returns an error */
+
+ gpos->mmfunc = default_mmfunc;
+
+ /* the default glyph function is TT_Load_Glyph() */
+
+ gpos->gfunc = FT_Load_Glyph;
+
+ gpos->loaded = FALSE;
+
+ return TT_Err_Ok;
+ }
+
+
+ static FT_Error GPOS_Destroy( void* ext,
+ PFace face )
+ {
+ TTO_GPOSHeader* gpos = (TTO_GPOSHeader*)ext;
+
+
+ /* by convention */
+
+ if ( !gpos )
+ return TT_Err_Ok;
+
+ if ( gpos->loaded )
+ {
+ Free_LookupList( &gpos->LookupList, GPOS );
+ Free_FeatureList( &gpos->FeatureList );
+ Free_ScriptList( &gpos->ScriptList );
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_Init_GPOS_Extension( TT_Engine engine )
+ {
+ PEngine_Instance _engine = HANDLE_Engine( engine );
+
+
+ if ( !_engine )
+ return TT_Err_Invalid_Engine;
+
+ return TT_Register_Extension( _engine,
+ GPOS_ID,
+ sizeof ( TTO_GPOSHeader ),
+ GPOS_Create,
+ GPOS_Destroy );
+ }
+#endif
+
+ EXPORT_FUNC
+ FT_Error TT_Load_GPOS_Table( FT_Face face,
+ TTO_GPOSHeader** retptr,
+ TTO_GDEFHeader* gdef )
+ {
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ FT_UShort i, num_lookups;
+ TTO_GPOSHeader* gpos;
+ TTO_Lookup* lo;
+ TT_Face tt_face = (TT_Face)face;
+
+ FT_Stream stream = face->stream;
+ FT_Error error;
+ FT_Memory memory = face->memory;
+
+
+ if ( !retptr )
+ return TT_Err_Invalid_Argument;
+
+ if ( !stream )
+ return TT_Err_Invalid_Face_Handle;
+
+ if (( error = tt_face->goto_table( tt_face, TTAG_GPOS, stream, 0 ) ))
+ return error;
+
+ base_offset = FILE_Pos();
+
+ if ( ALLOC ( gpos, sizeof( *gpos ) ) )
+ return error;
+
+ gpos->memory = memory;
+ gpos->gfunc = 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 = Load_ScriptList( &gpos->ScriptList,
+ stream ) ) != TT_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 = Load_FeatureList( &gpos->FeatureList,
+ stream ) ) != TT_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 = Load_LookupList( &gpos->LookupList,
+ stream, GPOS ) ) != TT_Err_Ok )
+ goto Fail2;
+
+ gpos->gdef = gdef; /* can be NULL */
+
+ /* 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 )
+ {
+ lo = gpos->LookupList.Lookup;
+ num_lookups = gpos->LookupList.LookupCount;
+
+ for ( i = 0; i < num_lookups; i++ )
+ {
+ if ( lo[i].LookupFlag & IGNORE_SPECIAL_MARKS )
+ {
+ if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) ||
+ ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( !new_offset )
+ return TTO_Err_Invalid_GDEF_SubTable;
+
+ new_offset += base_offset;
+
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ClassDefinition( &gdef->MarkAttachClassDef,
+ 256, stream ) ) != TT_Err_Ok )
+ goto Fail1;
+
+ break;
+ }
+ }
+ }
+
+ *retptr = gpos;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ Free_LookupList( &gpos->LookupList, GPOS, memory );
+
+ Fail2:
+ Free_FeatureList( &gpos->FeatureList, memory );
+
+ Fail3:
+ Free_ScriptList( &gpos->ScriptList, memory );
+
+ Fail4:
+ FREE( gpos );
+
+ return error;
+ }
+
+ EXPORT_FUNC
+ FT_Error TT_Done_GPOS_Table( TTO_GPOSHeader* gpos )
+ {
+ FT_Memory memory = gpos->memory;
+
+ Free_LookupList( &gpos->LookupList, GPOS, memory );
+ Free_FeatureList( &gpos->FeatureList, memory );
+ Free_ScriptList( &gpos->ScriptList, memory );
+
+ return FT_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 FT_Error Load_ValueRecord( TTO_ValueRecord* vr,
+ FT_UShort format,
+ FT_ULong base_offset,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_ULong cur_offset, new_offset;
+
+
+ if ( format & HAVE_X_PLACEMENT )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ vr->XPlacement = GET_Short();
+
+ FORGET_Frame();
+ }
+ else
+ vr->XPlacement = 0;
+
+ if ( format & HAVE_Y_PLACEMENT )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ vr->YPlacement = GET_Short();
+
+ FORGET_Frame();
+ }
+ else
+ vr->YPlacement = 0;
+
+ if ( format & HAVE_X_ADVANCE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ vr->XAdvance = GET_Short();
+
+ FORGET_Frame();
+ }
+ else
+ vr->XAdvance = 0;
+
+ if ( format & HAVE_Y_ADVANCE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ vr->YAdvance = GET_Short();
+
+ FORGET_Frame();
+ }
+ else
+ vr->YAdvance = 0;
+
+ if ( 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 = Load_Device( &vr->XPlacementDevice,
+ stream ) ) != TT_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 & 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 = Load_Device( &vr->YPlacementDevice,
+ stream ) ) != TT_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 & 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 = Load_Device( &vr->XAdvanceDevice,
+ stream ) ) != TT_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 & 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 = Load_Device( &vr->YAdvanceDevice,
+ stream ) ) != TT_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 & HAVE_X_ID_PLACEMENT )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ vr->XIdPlacement = GET_UShort();
+
+ FORGET_Frame();
+ }
+ else
+ vr->XIdPlacement = 0;
+
+ if ( format & HAVE_Y_ID_PLACEMENT )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ vr->YIdPlacement = GET_UShort();
+
+ FORGET_Frame();
+ }
+ else
+ vr->YIdPlacement = 0;
+
+ if ( format & HAVE_X_ID_ADVANCE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ vr->XIdAdvance = GET_UShort();
+
+ FORGET_Frame();
+ }
+ else
+ vr->XIdAdvance = 0;
+
+ if ( format & HAVE_Y_ID_ADVANCE )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ vr->YIdAdvance = GET_UShort();
+
+ FORGET_Frame();
+ }
+ else
+ vr->YIdAdvance = 0;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ Free_Device( &vr->YAdvanceDevice, memory );
+
+ Fail2:
+ Free_Device( &vr->XAdvanceDevice, memory );
+
+ Fail3:
+ Free_Device( &vr->YPlacementDevice, memory );
+ return error;
+ }
+
+
+ static void Free_ValueRecord( TTO_ValueRecord* vr,
+ FT_UShort format,
+ FT_Memory memory )
+ {
+ if ( format & HAVE_Y_ADVANCE_DEVICE )
+ Free_Device( &vr->YAdvanceDevice, memory );
+ if ( format & HAVE_X_ADVANCE_DEVICE )
+ Free_Device( &vr->XAdvanceDevice, memory );
+ if ( format & HAVE_Y_PLACEMENT_DEVICE )
+ Free_Device( &vr->YPlacementDevice, memory );
+ if ( format & HAVE_X_PLACEMENT_DEVICE )
+ Free_Device( &vr->XPlacementDevice, memory );
+ }
+
+
+ static FT_Error Get_ValueRecord( GPOS_Instance* gpi,
+ TTO_ValueRecord* vr,
+ FT_UShort format,
+ TTO_GPOS_Data* gd )
+ {
+ FT_Pos value;
+ FT_Short pixel_value;
+ FT_Error error = TT_Err_Ok;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ FT_UShort x_ppem, y_ppem;
+ FT_Fixed x_scale, y_scale;
+
+
+ if ( !format )
+ return TT_Err_Ok;
+
+ x_ppem = gpi->face->size->metrics.x_ppem;
+ y_ppem = gpi->face->size->metrics.y_ppem;
+ x_scale = gpi->face->size->metrics.x_scale;
+ y_scale = gpi->face->size->metrics.y_scale;
+
+ /* design units -> fractional pixel */
+
+ if ( format & HAVE_X_PLACEMENT )
+ gd->x_pos += x_scale * vr->XPlacement / 0x10000;
+ if ( format & HAVE_Y_PLACEMENT )
+ gd->y_pos += y_scale * vr->YPlacement / 0x10000;
+ if ( format & HAVE_X_ADVANCE )
+ gd->x_advance += x_scale * vr->XAdvance / 0x10000;
+ if ( format & HAVE_Y_ADVANCE )
+ gd->y_advance += y_scale * vr->YAdvance / 0x10000;
+
+ if ( !gpi->dvi )
+ {
+ /* pixel -> fractional pixel */
+
+ if ( format & HAVE_X_PLACEMENT_DEVICE )
+ {
+ Get_Device( &vr->XPlacementDevice, x_ppem, &pixel_value );
+ gd->x_pos += pixel_value << 6;
+ }
+ if ( format & HAVE_Y_PLACEMENT_DEVICE )
+ {
+ Get_Device( &vr->YPlacementDevice, y_ppem, &pixel_value );
+ gd->y_pos += pixel_value << 6;
+ }
+ if ( format & HAVE_X_ADVANCE_DEVICE )
+ {
+ Get_Device( &vr->XAdvanceDevice, x_ppem, &pixel_value );
+ gd->x_advance += pixel_value << 6;
+ }
+ if ( format & HAVE_Y_ADVANCE_DEVICE )
+ {
+ 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 & HAVE_X_ID_PLACEMENT )
+ {
+ error = (gpos->mmfunc)( gpi->face, vr->XIdPlacement,
+ &value, gpos->data );
+ if ( error )
+ return error;
+ gd->x_pos += value;
+ }
+ if ( format & HAVE_Y_ID_PLACEMENT )
+ {
+ error = (gpos->mmfunc)( gpi->face, vr->YIdPlacement,
+ &value, gpos->data );
+ if ( error )
+ return error;
+ gd->y_pos += value;
+ }
+ if ( format & HAVE_X_ID_ADVANCE )
+ {
+ error = (gpos->mmfunc)( gpi->face, vr->XIdAdvance,
+ &value, gpos->data );
+ if ( error )
+ return error;
+ gd->x_advance += value;
+ }
+ if ( format & HAVE_Y_ID_ADVANCE )
+ {
+ error = (gpos->mmfunc)( gpi->face, vr->YIdAdvance,
+ &value, gpos->data );
+ if ( error )
+ return error;
+ gd->y_advance += value;
+ }
+
+ return error;
+ }
+
+
+ /* AnchorFormat1 */
+ /* AnchorFormat2 */
+ /* AnchorFormat3 */
+ /* AnchorFormat4 */
+
+ static FT_Error Load_Anchor( TTO_Anchor* an,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_ULong 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 = Load_Device( &an->af.af3.XDeviceTable,
+ stream ) ) != TT_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 = 0;
+ }
+
+ 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 = Load_Device( &an->af.af3.YDeviceTable,
+ stream ) ) != TT_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 = 0;
+ }
+ 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 TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ Free_Device( &an->af.af3.XDeviceTable, memory );
+ return error;
+ }
+
+
+ static void Free_Anchor( TTO_Anchor* an,
+ FT_Memory memory)
+ {
+ if ( an->PosFormat == 3 )
+ {
+ Free_Device( &an->af.af3.YDeviceTable, memory );
+ Free_Device( &an->af.af3.XDeviceTable, memory );
+ }
+ }
+
+
+ static FT_Error Get_Anchor( GPOS_Instance* gpi,
+ TTO_Anchor* an,
+ FT_UShort glyph_index,
+ FT_Pos* x_value,
+ FT_Pos* y_value )
+ {
+ FT_Error error = TT_Err_Ok;
+
+ FT_Outline outline;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+ FT_UShort ap;
+
+ FT_Short pixel_value;
+ FT_UShort load_flags;
+
+ FT_UShort x_ppem, y_ppem;
+ FT_Fixed x_scale, y_scale;
+
+
+ x_ppem = gpi->face->size->metrics.x_ppem;
+ y_ppem = gpi->face->size->metrics.y_ppem;
+ x_scale = gpi->face->size->metrics.x_scale;
+ y_scale = gpi->face->size->metrics.y_scale;
+
+ switch ( an->PosFormat )
+ {
+ case 0:
+ /* The special case of an empty AnchorTable */
+
+ return TTO_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->face, glyph_index, load_flags );
+ if ( error )
+ return error;
+
+ if ( gpi->face->glyph->format != ft_glyph_format_outline )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ outline = gpi->face->glyph->outline;
+
+ /* if outline.n_points is set to zero by gfunc(), 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 TTO_Err_Invalid_GPOS_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 )
+ {
+ Get_Device( &an->af.af3.XDeviceTable, x_ppem, &pixel_value );
+ *x_value = pixel_value << 6;
+ 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->face, an->af.af4.XIdAnchor,
+ x_value, gpos->data );
+ if ( error )
+ return error;
+
+ error = (gpos->mmfunc)( gpi->face, an->af.af4.YIdAnchor,
+ y_value, gpos->data );
+ if ( error )
+ return error;
+ break;
+ }
+
+ return error;
+ }
+
+
+ /* MarkArray */
+
+ static FT_Error Load_MarkArray ( TTO_MarkArray* ma,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_Anchor( &mr[n].MarkAnchor, memory );
+
+ FREE( mr );
+ return error;
+ }
+
+
+ static void Free_MarkArray( TTO_MarkArray* ma,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_MarkRecord* mr;
+
+
+ if ( ma->MarkRecord )
+ {
+ count = ma->MarkCount;
+ mr = ma->MarkRecord;
+
+ for ( n = 0; n < count; n++ )
+ Free_Anchor( &mr[n].MarkAnchor, memory );
+
+ FREE( mr );
+ }
+ }
+
+
+ /* LookupType 1 */
+
+ /* SinglePosFormat1 */
+ /* SinglePosFormat2 */
+
+ FT_Error Load_SinglePos( TTO_SinglePos* sp,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count, format;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 TTO_Err_Invalid_GPOS_SubTable;
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Coverage( &sp->Coverage, stream ) ) != TT_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, TTO_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 TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_ValueRecord( &vr[n], format, memory );
+
+ FREE( vr );
+
+ Fail2:
+ Free_Coverage( &sp->Coverage, memory );
+ return error;
+ }
+
+
+ void Free_SinglePos( TTO_SinglePos* sp,
+ FT_Memory memory )
+ {
+ FT_UShort n, count, format;
+
+ TTO_ValueRecord* v;
+
+
+ format = sp->ValueFormat;
+
+ switch ( sp->PosFormat )
+ {
+ case 1:
+ Free_ValueRecord( &sp->spf.spf1.Value, format, memory );
+ 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, memory );
+
+ FREE( v );
+ }
+ break;
+ }
+
+ Free_Coverage( &sp->Coverage, memory );
+ }
+
+
+ static FT_Error Lookup_SinglePos( GPOS_Instance* gpi,
+ TTO_SinglePos* sp,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length )
+ {
+ FT_UShort index, property;
+ FT_Error error;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return TTO_Err_Not_Covered;
+
+ if ( CHECK_Property( gpos->gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &sp->Coverage, in->string[in->pos], &index );
+ if ( error )
+ return error;
+
+ switch ( sp->PosFormat )
+ {
+ case 1:
+ error = Get_ValueRecord( gpi, &sp->spf.spf1.Value,
+ sp->ValueFormat, &out[in->pos] );
+ if ( error )
+ return error;
+ break;
+
+ case 2:
+ if ( index >= sp->spf.spf2.ValueCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+ error = Get_ValueRecord( gpi, &sp->spf.spf2.Value[index],
+ sp->ValueFormat, &out[in->pos] );
+ if ( error )
+ return error;
+ break;
+
+ default:
+ return TTO_Err_Invalid_GPOS_SubTable;
+ }
+
+ (in->pos)++;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 2 */
+
+ /* PairSet */
+
+ static FT_Error Load_PairSet ( TTO_PairSet* ps,
+ FT_UShort format1,
+ FT_UShort format2,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong base_offset;
+
+ TTO_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, TTO_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 )
+ goto Fail;
+ }
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ {
+ if ( format1 )
+ Free_ValueRecord( &pvr[n].Value1, format1, memory );
+ if ( format2 )
+ Free_ValueRecord( &pvr[n].Value2, format2, memory );
+ }
+
+ FREE( pvr );
+ return error;
+ }
+
+
+ static void Free_PairSet( TTO_PairSet* ps,
+ FT_UShort format1,
+ FT_UShort format2,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_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, memory );
+ if ( format2 )
+ Free_ValueRecord( &pvr[n].Value2, format2, memory );
+ }
+
+ FREE( pvr );
+ }
+ }
+
+
+ /* PairPosFormat1 */
+
+ static FT_Error Load_PairPos1( TTO_PairPosFormat1* ppf1,
+ FT_UShort format1,
+ FT_UShort format2,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_PairSet ) )
+ goto Fail;
+
+ 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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_PairSet( &ps[n], format1, format2, memory );
+
+ FREE( ps );
+ return error;
+ }
+
+
+ static void Free_PairPos1( TTO_PairPosFormat1* ppf1,
+ FT_UShort format1,
+ FT_UShort format2,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_PairSet* ps;
+
+
+ if ( ppf1->PairSet )
+ {
+ count = ppf1->PairSetCount;
+ ps = ppf1->PairSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_PairSet( &ps[n], format1, format2, memory );
+
+ FREE( ps );
+ }
+ }
+
+
+ /* PairPosFormat2 */
+
+ static FT_Error Load_PairPos2( TTO_PairPosFormat2* ppf2,
+ FT_UShort format1,
+ FT_UShort format2,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort m, n, count1, count2;
+ FT_ULong cur_offset, new_offset1, new_offset2, base_offset;
+
+ TTO_Class1Record* c1r;
+ TTO_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 = Load_ClassDefinition( &ppf2->ClassDef1, count1,
+ stream ) ) != TT_Err_Ok )
+ return error;
+ if ( FILE_Seek( new_offset2 ) ||
+ ( error = Load_ClassDefinition( &ppf2->ClassDef2, count2,
+ stream ) ) != TT_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+
+ ppf2->Class1Record = NULL;
+
+ if ( ALLOC_ARRAY( ppf2->Class1Record, count1, TTO_Class1Record ) )
+ goto Fail1;
+
+ c1r = ppf2->Class1Record;
+
+ for ( m = 0; m < count1; m++ )
+ {
+ c1r[m].Class2Record = NULL;
+
+ if ( ALLOC_ARRAY( c1r[m].Class2Record, count2, TTO_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 Fail1;
+ }
+ if ( format2 )
+ {
+ error = Load_ValueRecord( &c2r[n].Value2, format2,
+ base_offset, stream );
+ if ( error )
+ goto Fail1;
+ }
+ }
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( m = 0; m < count1; m++ )
+ {
+ c2r = c1r[m].Class2Record;
+
+ for ( n = 0; n < count2; n++ )
+ {
+ if ( format1 )
+ Free_ValueRecord( &c2r[n].Value1, format1, memory );
+ if ( format2 )
+ Free_ValueRecord( &c2r[n].Value2, format2, memory );
+ }
+
+ FREE( c2r );
+ }
+
+ FREE( c1r );
+
+ Free_ClassDefinition( &ppf2->ClassDef2, memory );
+
+ Fail2:
+ Free_ClassDefinition( &ppf2->ClassDef1, memory );
+ return error;
+ }
+
+
+ static void Free_PairPos2( TTO_PairPosFormat2* ppf2,
+ FT_UShort format1,
+ FT_UShort format2,
+ FT_Memory memory )
+ {
+ FT_UShort m, n, count1, count2;
+
+ TTO_Class1Record* c1r;
+ TTO_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, memory );
+ if ( format2 )
+ Free_ValueRecord( &c2r[n].Value2, format2, memory );
+ }
+
+ FREE( c2r );
+ }
+
+ FREE( c1r );
+
+ Free_ClassDefinition( &ppf2->ClassDef2, memory );
+ Free_ClassDefinition( &ppf2->ClassDef1, memory );
+ }
+ }
+
+
+ FT_Error Load_PairPos( TTO_PairPos* pp,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort format1, format2;
+ FT_ULong 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 = Load_Coverage( &pp->Coverage, stream ) ) != TT_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 TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ Free_Coverage( &pp->Coverage, memory );
+ return error;
+ }
+
+
+ void Free_PairPos( TTO_PairPos* pp,
+ FT_Memory memory )
+ {
+ FT_UShort format1, format2;
+
+
+ format1 = pp->ValueFormat1;
+ format2 = pp->ValueFormat2;
+
+ switch ( pp->PosFormat )
+ {
+ case 1:
+ Free_PairPos1( &pp->ppf.ppf1, format1, format2, memory );
+ break;
+
+ case 2:
+ Free_PairPos2( &pp->ppf.ppf2, format1, format2, memory );
+ break;
+ }
+
+ Free_Coverage( &pp->Coverage, memory );
+ }
+
+
+ static FT_Error Lookup_PairPos1( GPOS_Instance* gpi,
+ TTO_PairPosFormat1* ppf1,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort first_pos,
+ FT_UShort index,
+ FT_UShort format1,
+ FT_UShort format2 )
+ {
+ FT_Error error;
+ FT_UShort numpvr, glyph2;
+
+ TTO_PairValueRecord* pvr;
+
+
+ if ( index >= ppf1->PairSetCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ pvr = ppf1->PairSet[index].PairValueRecord;
+ if ( !pvr )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ glyph2 = in->string[in->pos];
+
+ for ( numpvr = ppf1->PairSet[index].PairValueCount;
+ numpvr;
+ numpvr--, pvr++ )
+ {
+ if ( glyph2 == pvr->SecondGlyph )
+ {
+ error = Get_ValueRecord( gpi, &pvr->Value1, format1,
+ &out[first_pos] );
+ if ( error )
+ return error;
+ return Get_ValueRecord( gpi, &pvr->Value2, format2,
+ &out[in->pos] );
+ }
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ static FT_Error Lookup_PairPos2( GPOS_Instance* gpi,
+ TTO_PairPosFormat2* ppf2,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort first_pos,
+ FT_UShort format1,
+ FT_UShort format2 )
+ {
+ FT_Error error;
+ FT_UShort cl1, cl2;
+
+ TTO_Class1Record* c1r;
+ TTO_Class2Record* c2r;
+
+
+ error = Get_Class( &ppf2->ClassDef1, in->string[first_pos],
+ &cl1, NULL );
+ if ( error )
+ return error;
+ error = Get_Class( &ppf2->ClassDef2, in->string[in->pos],
+ &cl2, NULL );
+ if ( error )
+ return error;
+
+ c1r = &ppf2->Class1Record[cl1];
+ if ( !c1r )
+ return TTO_Err_Invalid_GPOS_SubTable;
+ c2r = &c1r->Class2Record[cl2];
+
+ error = Get_ValueRecord( gpi, &c2r->Value1, format1, &out[first_pos] );
+ if ( error )
+ return error;
+ return Get_ValueRecord( gpi, &c2r->Value2, format2, &out[in->pos] );
+ }
+
+
+ static FT_Error Lookup_PairPos( GPOS_Instance* gpi,
+ TTO_PairPos* pp,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length )
+ {
+ FT_Error error;
+ FT_UShort index, property, first_pos;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+
+ if ( in->pos >= in->length )
+ return TTO_Err_Not_Covered; /* Not enough glyphs in stream */
+
+ if ( context_length != 0xFFFF && context_length < 2 )
+ return TTO_Err_Not_Covered;
+
+ if ( CHECK_Property( gpos->gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &pp->Coverage, in->string[in->pos], &index );
+ if ( error )
+ return error;
+
+ /* second glyph */
+
+ first_pos = in->pos;
+ (in->pos)++;
+
+ while ( CHECK_Property( gpos->gdef, in->string[in->pos],
+ flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( in->pos < in->length )
+ (in->pos)++;
+ else
+ break;
+ }
+
+ switch ( pp->PosFormat )
+ {
+ case 1:
+ error = Lookup_PairPos1( gpi, &pp->ppf.ppf1, in, out,
+ first_pos, index,
+ pp->ValueFormat1, pp->ValueFormat2 );
+ break;
+
+ case 2:
+ error = Lookup_PairPos2( gpi, &pp->ppf.ppf2, in, out, first_pos,
+ pp->ValueFormat1, pp->ValueFormat2 );
+ break;
+
+ default:
+ return TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ /* adjusting the `next' glyph */
+
+ if ( pp->ValueFormat2 )
+ (in->pos)++;
+
+ return error;
+ }
+
+
+ /* LookupType 3 */
+
+ /* CursivePosFormat1 */
+
+ FT_Error Load_CursivePos( TTO_CursivePos* cp,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &cp->Coverage, stream ) ) != TT_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, TTO_EntryExitRecord ) )
+ goto Fail2;
+
+ eer = cp->EntryExitRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ 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].EntryAnchor,
+ stream ) ) != TT_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ eer[n].ExitAnchor.PosFormat = 0;
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ {
+ Free_Anchor( &eer[n].EntryAnchor, memory );
+ Free_Anchor( &eer[n].ExitAnchor, memory );
+ }
+
+ FREE( eer );
+
+ Fail2:
+ Free_Coverage( &cp->Coverage, memory );
+ return error;
+ }
+
+
+ void Free_CursivePos( TTO_CursivePos* cp,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_EntryExitRecord* eer;
+
+
+ if ( cp->EntryExitRecord )
+ {
+ count = cp->EntryExitCount;
+ eer = cp->EntryExitRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ Free_Anchor( &eer[n].EntryAnchor, memory );
+ Free_Anchor( &eer[n].ExitAnchor, memory );
+ }
+
+ FREE( eer );
+ }
+
+ Free_Coverage( &cp->Coverage, memory );
+ }
+
+
+ static FT_Error Lookup_CursivePos( GPOS_Instance* gpi,
+ TTO_CursivePos* cp,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length )
+ {
+ FT_UShort index, property;
+ FT_Error error;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_EntryExitRecord* eer;
+ FT_Pos entry_x, entry_y;
+ FT_Pos exit_x, exit_y;
+
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ {
+ gpi->last = 0xFFFF;
+ return TTO_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->gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ /* We don't handle mark glyphs here. According to Andrei, this isn't
+ possible, but who knows... */
+
+ if ( property == MARK_GLYPH )
+ {
+ gpi->last = 0xFFFF;
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &cp->Coverage, in->string[in->pos], &index );
+ if ( error )
+ {
+ gpi->last = 0xFFFF;
+ return error;
+ }
+
+ if ( index >= cp->EntryExitCount )
+ return TTO_Err_Invalid_GPOS_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 TTO_Err_Not_Covered if there is no anchor
+ table. */
+
+ error = Get_Anchor( gpi, &eer->EntryAnchor, in->string[in->pos],
+ &entry_x, &entry_y );
+ if ( error == TTO_Err_Not_Covered )
+ goto end;
+ if ( error )
+ return error;
+
+ if ( gpi->r2l )
+ {
+ out[in->pos].x_advance = entry_x - gpi->anchor_x;
+ out[in->pos].new_advance = TRUE;
+ }
+ else
+ {
+ out[gpi->last].x_advance = gpi->anchor_x - entry_x;
+ out[gpi->last].new_advance = TRUE;
+ }
+
+ out[in->pos].y_pos = gpi->anchor_y - entry_y + out[gpi->last].y_pos;
+
+ end:
+ error = Get_Anchor( gpi, &eer->ExitAnchor, in->string[in->pos],
+ &exit_x, &exit_y );
+ if ( error == TTO_Err_Not_Covered )
+ gpi->last = 0xFFFF;
+ else
+ {
+ gpi->last = in->pos;
+ gpi->anchor_x = exit_x;
+ gpi->anchor_y = exit_y;
+ }
+ if ( error )
+ return error;
+
+ (in->pos)++;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 4 */
+
+ /* BaseArray */
+
+ static FT_Error Load_BaseArray( TTO_BaseArray* ba,
+ FT_UShort num_classes,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort m, n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_BaseRecord* br;
+ TTO_Anchor* ban;
+
+
+ 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, TTO_BaseRecord ) )
+ return error;
+
+ br = ba->BaseRecord;
+
+ for ( m = 0; m < count; m++ )
+ {
+ br[m].BaseAnchor = NULL;
+
+ if ( ALLOC_ARRAY( br[m].BaseAnchor, num_classes, TTO_Anchor ) )
+ goto Fail;
+
+ ban = br[m].BaseAnchor;
+
+ for ( n = 0; n < num_classes; 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_Anchor( &ban[n], stream ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( m = 0; m < count; m++ )
+ {
+ ban = br[m].BaseAnchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ Free_Anchor( &ban[n], memory );
+
+ FREE( ban );
+ }
+
+ FREE( br );
+ return error;
+ }
+
+
+ static void Free_BaseArray( TTO_BaseArray* ba,
+ FT_UShort num_classes,
+ FT_Memory memory )
+ {
+ FT_UShort m, n, count;
+
+ TTO_BaseRecord* br;
+ TTO_Anchor* ban;
+
+
+ if ( ba->BaseRecord )
+ {
+ count = ba->BaseCount;
+ br = ba->BaseRecord;
+
+ for ( m = 0; m < count; m++ )
+ {
+ ban = br[m].BaseAnchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ Free_Anchor( &ban[n], memory );
+
+ FREE( ban );
+ }
+
+ FREE( br );
+ }
+ }
+
+
+ /* MarkBasePosFormat1 */
+
+ FT_Error Load_MarkBasePos( TTO_MarkBasePos* mbp,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_ULong 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();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Coverage( &mbp->MarkCoverage, stream ) ) != TT_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 = Load_Coverage( &mbp->BaseCoverage, stream ) ) != TT_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 ) ) != TT_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ Free_MarkArray( &mbp->MarkArray, memory );
+
+ Fail2:
+ Free_Coverage( &mbp->BaseCoverage, memory );
+
+ Fail3:
+ Free_Coverage( &mbp->MarkCoverage, memory );
+ return error;
+ }
+
+
+ void Free_MarkBasePos( TTO_MarkBasePos* mbp,
+ FT_Memory memory )
+ {
+ Free_BaseArray( &mbp->BaseArray, mbp->ClassCount, memory );
+ Free_MarkArray( &mbp->MarkArray, memory );
+ Free_Coverage( &mbp->BaseCoverage, memory );
+ Free_Coverage( &mbp->MarkCoverage, memory );
+ }
+
+
+ static FT_Error Lookup_MarkBasePos( GPOS_Instance* gpi,
+ TTO_MarkBasePos* mbp,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length )
+ {
+ FT_UShort i, j, mark_index, base_index, property, class;
+ FT_Pos x_mark_value, y_mark_value, x_base_value, y_base_value;
+ FT_Error error;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_MarkArray* ma;
+ TTO_BaseArray* ba;
+ TTO_BaseRecord* br;
+ TTO_Anchor* mark_anchor;
+ TTO_Anchor* base_anchor;
+
+ TTO_GPOS_Data* o;
+
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return TTO_Err_Not_Covered;
+
+ if ( flags & IGNORE_BASE_GLYPHS )
+ return TTO_Err_Not_Covered;
+
+ if ( CHECK_Property( gpos->gdef, in->string[in->pos],
+ flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &mbp->MarkCoverage, in->string[in->pos],
+ &mark_index );
+ if ( error )
+ return error;
+
+ /* now we search backwards for a base glyph */
+
+ i = 1;
+ j = in->pos - 1;
+
+ while ( i <= in->pos )
+ {
+ error = TT_GDEF_Get_Glyph_Property( gpos->gdef, in->string[j],
+ &property );
+ if ( error )
+ return error;
+
+ if ( property != TTO_MARK )
+ break;
+
+ i++;
+ j--;
+ }
+
+ if ( property != TTO_BASE_GLYPH )
+ return TTO_Err_Not_Covered;
+
+ if ( i > in->pos )
+ return TTO_Err_Not_Covered;
+
+ error = Coverage_Index( &mbp->BaseCoverage, in->string[j],
+ &base_index );
+ if ( error )
+ return error;
+
+ ma = &mbp->MarkArray;
+
+ if ( mark_index >= ma->MarkCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ class = ma->MarkRecord[mark_index].Class;
+ mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
+
+ if ( class >= mbp->ClassCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ ba = &mbp->BaseArray;
+
+ if ( base_index >= ba->BaseCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ br = &ba->BaseRecord[base_index];
+ base_anchor = &br->BaseAnchor[class];
+
+ error = Get_Anchor( gpi, mark_anchor, in->string[in->pos],
+ &x_mark_value, &y_mark_value );
+ if ( error )
+ return error;
+ error = Get_Anchor( gpi, base_anchor, in->string[j],
+ &x_base_value, &y_base_value );
+ if ( error )
+ return error;
+
+ /* anchor points are not cumulative */
+
+ o = &out[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;
+
+ (in->pos)++;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 5 */
+
+ /* LigatureAttach */
+
+ static FT_Error Load_LigatureAttach( TTO_LigatureAttach* lat,
+ FT_UShort num_classes,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort m, n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_ComponentRecord* cr;
+ TTO_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, TTO_ComponentRecord ) )
+ return error;
+
+ cr = lat->ComponentRecord;
+
+ for ( m = 0; m < count; m++ )
+ {
+ cr[m].LigatureAnchor = NULL;
+
+ if ( ALLOC_ARRAY( cr[m].LigatureAnchor, num_classes, TTO_Anchor ) )
+ goto Fail;
+
+ lan = cr[m].LigatureAnchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ {
+ 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 = Load_Anchor( &lan[n], stream ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+ else
+ lan[n].PosFormat = 0;
+ }
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( m = 0; m < count; m++ )
+ {
+ lan = cr[m].LigatureAnchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ Free_Anchor( &lan[n], memory );
+
+ FREE( lan );
+ }
+
+ FREE( cr );
+ return error;
+ }
+
+
+ static void Free_LigatureAttach( TTO_LigatureAttach* lat,
+ FT_UShort num_classes,
+ FT_Memory memory )
+ {
+ FT_UShort m, n, count;
+
+ TTO_ComponentRecord* cr;
+ TTO_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], memory );
+
+ FREE( lan );
+ }
+
+ FREE( cr );
+ }
+ }
+
+
+ /* LigatureArray */
+
+ static FT_Error Load_LigatureArray( TTO_LigatureArray* la,
+ FT_UShort num_classes,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_LigatureAttach( &lat[n], num_classes, memory );
+
+ FREE( lat );
+ return error;
+ }
+
+
+ static void Free_LigatureArray( TTO_LigatureArray* la,
+ FT_UShort num_classes,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_LigatureAttach* lat;
+
+
+ if ( la->LigatureAttach )
+ {
+ count = la->LigatureCount;
+ lat = la->LigatureAttach;
+
+ for ( n = 0; n < count; n++ )
+ Free_LigatureAttach( &lat[n], num_classes, memory );
+
+ FREE( lat );
+ }
+ }
+
+
+ /* MarkLigPosFormat1 */
+
+ FT_Error Load_MarkLigPos( TTO_MarkLigPos* mlp,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_ULong 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 = Load_Coverage( &mlp->MarkCoverage, stream ) ) != TT_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 = Load_Coverage( &mlp->LigatureCoverage,
+ stream ) ) != TT_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 ) ) != TT_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ Free_MarkArray( &mlp->MarkArray, memory );
+
+ Fail2:
+ Free_Coverage( &mlp->LigatureCoverage, memory );
+
+ Fail3:
+ Free_Coverage( &mlp->MarkCoverage, memory );
+ return error;
+ }
+
+
+ void Free_MarkLigPos( TTO_MarkLigPos* mlp,
+ FT_Memory memory)
+ {
+ Free_LigatureArray( &mlp->LigatureArray, mlp->ClassCount, memory );
+ Free_MarkArray( &mlp->MarkArray, memory );
+ Free_Coverage( &mlp->LigatureCoverage, memory );
+ Free_Coverage( &mlp->MarkCoverage, memory );
+ }
+
+
+ static FT_Error Lookup_MarkLigPos( GPOS_Instance* gpi,
+ TTO_MarkLigPos* mlp,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length )
+ {
+ FT_UShort i, j, mark_index, lig_index, property, class;
+ FT_UShort mark_glyph;
+ FT_Pos x_mark_value, y_mark_value, x_lig_value, y_lig_value;
+ FT_Error error;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_MarkArray* ma;
+ TTO_LigatureArray* la;
+ TTO_LigatureAttach* lat;
+ TTO_ComponentRecord* cr;
+ FT_UShort comp_index;
+ TTO_Anchor* mark_anchor;
+ TTO_Anchor* lig_anchor;
+
+ TTO_GPOS_Data* o;
+
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return TTO_Err_Not_Covered;
+
+ if ( flags & IGNORE_LIGATURES )
+ return TTO_Err_Not_Covered;
+
+ mark_glyph = in->string[in->pos];
+
+ if ( CHECK_Property( gpos->gdef, mark_glyph, flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &mlp->MarkCoverage, mark_glyph, &mark_index );
+ if ( error )
+ return error;
+
+ /* now we search backwards for a ligature */
+
+ i = 1;
+ j = in->pos - 1;
+
+ while ( i <= in->pos )
+ {
+ error = TT_GDEF_Get_Glyph_Property( gpos->gdef, in->string[j],
+ &property );
+ if ( error )
+ return error;
+
+ if ( property != TTO_MARK )
+ break;
+
+ i++;
+ j--;
+ }
+
+ if ( property != TTO_LIGATURE )
+ return TTO_Err_Not_Covered;
+
+ if ( i > in->pos )
+ return TTO_Err_Not_Covered;
+
+ error = Coverage_Index( &mlp->LigatureCoverage, in->string[j],
+ &lig_index );
+ if ( error )
+ return error;
+
+ ma = &mlp->MarkArray;
+
+ if ( mark_index >= ma->MarkCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ class = ma->MarkRecord[mark_index].Class;
+ mark_anchor = &ma->MarkRecord[mark_index].MarkAnchor;
+
+ if ( class >= mlp->ClassCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ la = &mlp->LigatureArray;
+
+ if ( lig_index >= la->LigatureCount )
+ return TTO_Err_Invalid_GPOS_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->ligIDs && in->components &&
+ in->ligIDs[j] == in->ligIDs[in->pos] )
+ {
+ comp_index = in->components[in->pos];
+ if ( comp_index >= lat->ComponentCount )
+ return TTO_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->string[in->pos],
+ &x_mark_value, &y_mark_value );
+ if ( error )
+ return error;
+ error = Get_Anchor( gpi, lig_anchor, in->string[j],
+ &x_lig_value, &y_lig_value );
+ if ( error )
+ return error;
+
+ /* anchor points are not cumulative */
+
+ o = &out[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;
+
+ (in->pos)++;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 6 */
+
+ /* Mark2Array */
+
+ static FT_Error Load_Mark2Array( TTO_Mark2Array* m2a,
+ FT_UShort num_classes,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort m, n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_Mark2Record* m2r;
+ TTO_Anchor* m2an;
+
+
+ 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, TTO_Mark2Record ) )
+ return error;
+
+ m2r = m2a->Mark2Record;
+
+ for ( m = 0; m < count; m++ )
+ {
+ m2r[m].Mark2Anchor = NULL;
+
+ if ( ALLOC_ARRAY( m2r[m].Mark2Anchor, num_classes, TTO_Anchor ) )
+ goto Fail;
+
+ m2an = m2r[m].Mark2Anchor;
+
+ for ( n = 0; n < num_classes; 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_Anchor( &m2an[n], stream ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( m = 0; m < count; m++ )
+ {
+ m2an = m2r[m].Mark2Anchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ Free_Anchor( &m2an[n], memory );
+
+ FREE( m2an );
+ }
+
+ FREE( m2r );
+ return error;
+ }
+
+
+ static void Free_Mark2Array( TTO_Mark2Array* m2a,
+ FT_UShort num_classes,
+ FT_Memory memory )
+ {
+ FT_UShort m, n, count;
+
+ TTO_Mark2Record* m2r;
+ TTO_Anchor* m2an;
+
+
+ if ( m2a->Mark2Record )
+ {
+ count = m2a->Mark2Count;
+ m2r = m2a->Mark2Record;
+
+ for ( m = 0; m < count; m++ )
+ {
+ m2an = m2r[m].Mark2Anchor;
+
+ for ( n = 0; n < num_classes; n++ )
+ Free_Anchor( &m2an[n], memory );
+
+ FREE( m2an );
+ }
+
+ FREE( m2r );
+ }
+ }
+
+
+ /* MarkMarkPosFormat1 */
+
+ FT_Error Load_MarkMarkPos( TTO_MarkMarkPos* mmp,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_ULong 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 = Load_Coverage( &mmp->Mark1Coverage,
+ stream ) ) != TT_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 = Load_Coverage( &mmp->Mark2Coverage,
+ stream ) ) != TT_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 ) ) != TT_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ Free_MarkArray( &mmp->Mark1Array, memory );
+
+ Fail2:
+ Free_Coverage( &mmp->Mark2Coverage, memory );
+
+ Fail3:
+ Free_Coverage( &mmp->Mark1Coverage, memory );
+ return error;
+ }
+
+
+ void Free_MarkMarkPos( TTO_MarkMarkPos* mmp,
+ FT_Memory memory)
+ {
+ Free_Mark2Array( &mmp->Mark2Array, mmp->ClassCount, memory );
+ Free_MarkArray( &mmp->Mark1Array, memory );
+ Free_Coverage( &mmp->Mark2Coverage, memory );
+ Free_Coverage( &mmp->Mark1Coverage, memory );
+ }
+
+
+ static FT_Error Lookup_MarkMarkPos( GPOS_Instance* gpi,
+ TTO_MarkMarkPos* mmp,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length )
+ {
+ FT_UShort j, mark1_index, mark2_index, property, class;
+ FT_Pos x_mark1_value, y_mark1_value,
+ x_mark2_value, y_mark2_value;
+ FT_Error error;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_MarkArray* ma1;
+ TTO_Mark2Array* ma2;
+ TTO_Mark2Record* m2r;
+ TTO_Anchor* mark1_anchor;
+ TTO_Anchor* mark2_anchor;
+
+ TTO_GPOS_Data* o;
+
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return TTO_Err_Not_Covered;
+
+ if ( flags & IGNORE_MARKS )
+ return TTO_Err_Not_Covered;
+
+ if ( CHECK_Property( gpos->gdef, in->string[in->pos],
+ flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &mmp->Mark1Coverage, in->string[in->pos],
+ &mark1_index );
+ if ( error )
+ return error;
+
+ /* now we check the preceding glyph whether it is a suitable
+ mark glyph */
+
+ if ( in->pos == 0 )
+ return TTO_Err_Not_Covered;
+
+ j = in->pos - 1;
+ error = TT_GDEF_Get_Glyph_Property( gpos->gdef, in->string[j],
+ &property );
+ if ( error )
+ return error;
+
+ if ( flags & IGNORE_SPECIAL_MARKS )
+ {
+ if ( property != (flags & 0xFF00) )
+ return TTO_Err_Not_Covered;
+ }
+ else
+ {
+ if ( property != TTO_MARK )
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &mmp->Mark2Coverage, in->string[j],
+ &mark2_index );
+ if ( error )
+ return error;
+
+ ma1 = &mmp->Mark1Array;
+
+ if ( mark1_index >= ma1->MarkCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ class = ma1->MarkRecord[mark1_index].Class;
+ mark1_anchor = &ma1->MarkRecord[mark1_index].MarkAnchor;
+
+ if ( class >= mmp->ClassCount )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ ma2 = &mmp->Mark2Array;
+
+ if ( mark2_index >= ma2->Mark2Count )
+ return TTO_Err_Invalid_GPOS_SubTable;
+
+ m2r = &ma2->Mark2Record[mark2_index];
+ mark2_anchor = &m2r->Mark2Anchor[class];
+
+ error = Get_Anchor( gpi, mark1_anchor, in->string[in->pos],
+ &x_mark1_value, &y_mark1_value );
+ if ( error )
+ return error;
+ error = Get_Anchor( gpi, mark2_anchor, in->string[j],
+ &x_mark2_value, &y_mark2_value );
+ if ( error )
+ return error;
+
+ /* anchor points are not cumulative */
+
+ o = &out[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;
+
+ (in->pos)++;
+
+ return TT_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 FT_Error Do_ContextPos( GPOS_Instance* gpi,
+ FT_UShort GlyphCount,
+ FT_UShort PosCount,
+ TTO_PosLookupRecord* pos,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ int nesting_level )
+ {
+ FT_Error error;
+ FT_UShort i, old_pos;
+
+
+ i = 0;
+
+ while ( i < GlyphCount )
+ {
+ if ( PosCount && i == pos->SequenceIndex )
+ {
+ old_pos = in->pos;
+
+ /* Do a positioning */
+
+ error = Do_Glyph_Lookup( gpi, pos->LookupListIndex, in, out,
+ GlyphCount, nesting_level );
+
+ if ( error )
+ return error;
+
+ pos++;
+ PosCount--;
+ i += in->pos - old_pos;
+ }
+ else
+ {
+ i++;
+ (in->pos)++;
+ }
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 7 */
+
+ /* PosRule */
+
+ static FT_Error Load_PosRule( TTO_PosRule* pr,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_UShort* i;
+
+ TTO_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, FT_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, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( plr );
+
+ Fail2:
+ FREE( i );
+ return error;
+ }
+
+
+ static void Free_PosRule( TTO_PosRule* pr,
+ FT_Memory memory )
+ {
+ FREE( pr->PosLookupRecord );
+ FREE( pr->Input );
+ }
+
+
+ /* PosRuleSet */
+
+ static FT_Error Load_PosRuleSet( TTO_PosRuleSet* prs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_PosRule( &pr[n], memory );
+
+ FREE( pr );
+ return error;
+ }
+
+
+ static void Free_PosRuleSet( TTO_PosRuleSet* prs,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_PosRule* pr;
+
+
+ if ( prs->PosRule )
+ {
+ count = prs->PosRuleCount;
+ pr = prs->PosRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_PosRule( &pr[n], memory );
+
+ FREE( pr );
+ }
+ }
+
+
+ /* ContextPosFormat1 */
+
+ static FT_Error Load_ContextPos1( TTO_ContextPosFormat1* cpf1,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &cpf1->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_PosRuleSet( &prs[n], memory );
+
+ FREE( prs );
+
+ Fail2:
+ Free_Coverage( &cpf1->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_Context1( TTO_ContextPosFormat1* cpf1,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_PosRuleSet* prs;
+
+
+ if ( cpf1->PosRuleSet )
+ {
+ count = cpf1->PosRuleSetCount;
+ prs = cpf1->PosRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_PosRuleSet( &prs[n], memory );
+
+ FREE( prs );
+ }
+
+ Free_Coverage( &cpf1->Coverage, memory );
+ }
+
+
+ /* PosClassRule */
+
+ static FT_Error Load_PosClassRule( TTO_ContextPosFormat2* cpf2,
+ TTO_PosClassRule* pcr,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ FT_UShort* c;
+ TTO_PosLookupRecord* plr;
+ FT_Bool* d;
+
+
+ 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, FT_UShort ) )
+ return error;
+
+ c = pcr->Class;
+ d = cpf2->ClassDef.Defined;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ {
+ c[n] = GET_UShort();
+
+ /* We check whether the specific class is used at all. If not,
+ class 0 is used instead. */
+
+ if ( !d[c[n]] )
+ c[n] = 0;
+ }
+
+ FORGET_Frame();
+
+ pcr->PosLookupRecord = NULL;
+
+ count = pcr->PosCount;
+
+ if ( ALLOC_ARRAY( pcr->PosLookupRecord, count, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( plr );
+
+ Fail2:
+ FREE( c );
+ return error;
+ }
+
+
+ static void Free_PosClassRule( TTO_PosClassRule* pcr,
+ FT_Memory memory )
+ {
+ FREE( pcr->PosLookupRecord );
+ FREE( pcr->Class );
+ }
+
+
+ /* PosClassSet */
+
+ static FT_Error Load_PosClassSet( TTO_ContextPosFormat2* cpf2,
+ TTO_PosClassSet* pcs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_PosClassRule( &pcr[n], memory );
+
+ FREE( pcr );
+ return error;
+ }
+
+
+ static void Free_PosClassSet( TTO_PosClassSet* pcs,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_PosClassRule* pcr;
+
+
+ if ( pcs->PosClassRule )
+ {
+ count = pcs->PosClassRuleCount;
+ pcr = pcs->PosClassRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_PosClassRule( &pcr[n], memory );
+
+ FREE( pcr );
+ }
+ }
+
+
+ /* ContextPosFormat2 */
+
+ static FT_Error Load_ContextPos2( TTO_ContextPosFormat2* cpf2,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &cpf2->Coverage, stream ) ) != TT_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 = Load_ClassDefinition( &cpf2->ClassDef, count,
+ stream ) ) != TT_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ cpf2->PosClassSet = NULL;
+ cpf2->MaxContextLength = 0;
+
+ if ( ALLOC_ARRAY( cpf2->PosClassSet, count, TTO_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 ) ) != TT_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 TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_PosClassSet( &pcs[n], memory );
+
+ FREE( pcs );
+
+ Fail2:
+ Free_ClassDefinition( &cpf2->ClassDef, memory );
+
+ Fail3:
+ Free_Coverage( &cpf2->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_Context2( TTO_ContextPosFormat2* cpf2,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_PosClassSet* pcs;
+
+
+ if ( cpf2->PosClassSet )
+ {
+ count = cpf2->PosClassSetCount;
+ pcs = cpf2->PosClassSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_PosClassSet( &pcs[n], memory );
+
+ FREE( pcs );
+ }
+
+ Free_ClassDefinition( &cpf2->ClassDef, memory );
+ Free_Coverage( &cpf2->Coverage, memory );
+ }
+
+
+ /* ContextPosFormat3 */
+
+ static FT_Error Load_ContextPos3( TTO_ContextPosFormat3* cpf3,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_Coverage* c;
+ TTO_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, TTO_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 = Load_Coverage( &c[n], stream ) ) != TT_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ cpf3->PosLookupRecord = NULL;
+
+ count = cpf3->PosCount;
+
+ if ( ALLOC_ARRAY( cpf3->PosLookupRecord, count, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( plr );
+
+ Fail2:
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ return error;
+ }
+
+
+ static void Free_Context3( TTO_ContextPosFormat3* cpf3,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_Coverage* c;
+
+
+ FREE( cpf3->PosLookupRecord );
+
+ if ( cpf3->Coverage )
+ {
+ count = cpf3->GlyphCount;
+ c = cpf3->Coverage;
+
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ }
+ }
+
+
+ /* ContextPos */
+
+ FT_Error Load_ContextPos( TTO_ContextPos* cp,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ 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 TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+ void Free_ContextPos( TTO_ContextPos* cp,
+ FT_Memory memory )
+ {
+ switch ( cp->PosFormat )
+ {
+ case 1:
+ Free_Context1( &cp->cpf.cpf1, memory );
+ break;
+
+ case 2:
+ Free_Context2( &cp->cpf.cpf2, memory );
+ break;
+
+ case 3:
+ Free_Context3( &cp->cpf.cpf3, memory );
+ break;
+ }
+ }
+
+
+ static FT_Error Lookup_ContextPos1( GPOS_Instance* gpi,
+ TTO_ContextPosFormat1* cpf1,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, property;
+ FT_UShort i, j, k, numpr;
+ FT_Error error;
+ FT_UShort* s_in;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_PosRule* pr;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gpos->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &cpf1->Coverage, in->string[in->pos], &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 )
+ continue;
+
+ if ( in->pos + pr[k].GlyphCount > in->length )
+ continue; /* context is too long */
+
+ s_in = &in->string[in->pos];
+
+ for ( i = 1, j = 1; i < pr[k].GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( in->pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( s_in[j] != pr[k].Input[i - 1] )
+ break;
+ }
+
+ if ( i == pr[k].GlyphCount )
+ return Do_ContextPos( gpi, pr[k].GlyphCount,
+ pr[k].PosCount, pr[k].PosLookupRecord,
+ in, out,
+ nesting_level );
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ static FT_Error Lookup_ContextPos2( GPOS_Instance* gpi,
+ TTO_ContextPosFormat2* cpf2,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, property;
+ FT_Error error;
+ FT_Memory memory = gpi->face->memory;
+ FT_UShort i, j, k, known_classes;
+
+ FT_UShort* classes;
+ FT_UShort* s_in;
+ FT_UShort* cl;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_PosClassSet* pcs;
+ TTO_PosClassRule* pr;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gpos->gdef;
+
+ if ( ALLOC_ARRAY( classes, cpf2->MaxContextLength, FT_UShort ) )
+ return error;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], 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 = Coverage_Index( &cpf2->Coverage, in->string[in->pos], &index );
+ if ( error )
+ goto End;
+
+ error = Get_Class( &cpf2->ClassDef, in->string[in->pos],
+ &classes[0], NULL );
+ if ( error )
+ goto End;
+ known_classes = 0;
+
+ pcs = &cpf2->PosClassSet[classes[0]];
+ if ( !pcs )
+ {
+ error = TTO_Err_Invalid_GPOS_SubTable;
+ goto End;
+ }
+
+ for ( k = 0; k < pcs->PosClassRuleCount; k++ )
+ {
+ pr = &pcs->PosClassRule[k];
+
+ if ( context_length != 0xFFFF && context_length < pr->GlyphCount )
+ continue;
+
+ if ( in->pos + pr->GlyphCount > in->length )
+ continue; /* context is too long */
+
+ s_in = &in->string[in->pos];
+ cl = pr->Class;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = 1; i < pr->GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( in->pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( i > known_classes )
+ {
+ /* Keeps us from having to do this for each rule */
+
+ error = Get_Class( &cpf2->ClassDef, s_in[j], &classes[i], NULL );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ known_classes = i;
+ }
+
+ if ( cl[i - 1] != classes[i] )
+ break;
+ }
+
+ if ( i == pr->GlyphCount )
+ {
+ error = Do_ContextPos( gpi, pr->GlyphCount,
+ pr->PosCount, pr->PosLookupRecord,
+ in, out,
+ nesting_level );
+ goto End;
+ }
+ }
+
+ error = TTO_Err_Not_Covered;
+
+ End:
+ FREE( classes );
+ return error;
+ }
+
+
+ static FT_Error Lookup_ContextPos3( GPOS_Instance* gpi,
+ TTO_ContextPosFormat3* cpf3,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_Error error;
+ FT_UShort index, i, j, property;
+ FT_UShort* s_in;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_Coverage* c;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gpos->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ if ( context_length != 0xFFFF && context_length < cpf3->GlyphCount )
+ return TTO_Err_Not_Covered;
+
+ if ( in->pos + cpf3->GlyphCount > in->length )
+ return TTO_Err_Not_Covered; /* context is too long */
+
+ s_in = &in->string[in->pos];
+ c = cpf3->Coverage;
+
+ for ( i = 1, j = 1; i < cpf3->GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( in->pos + j < in->length )
+ j++;
+ else
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &c[i], s_in[j], &index );
+ if ( error )
+ return error;
+ }
+
+ return Do_ContextPos( gpi, cpf3->GlyphCount,
+ cpf3->PosCount, cpf3->PosLookupRecord,
+ in, out,
+ nesting_level );
+ }
+
+
+ static FT_Error Lookup_ContextPos( GPOS_Instance* gpi,
+ TTO_ContextPos* cp,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ switch ( cp->PosFormat )
+ {
+ case 1:
+ return Lookup_ContextPos1( gpi, &cp->cpf.cpf1, in, out,
+ flags, context_length, nesting_level );
+
+ case 2:
+ return Lookup_ContextPos2( gpi, &cp->cpf.cpf2, in, out,
+ flags, context_length, nesting_level );
+
+ case 3:
+ return Lookup_ContextPos3( gpi, &cp->cpf.cpf3, in, out,
+ flags, context_length, nesting_level );
+
+ default:
+ return TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+ /* LookupType 8 */
+
+ /* ChainPosRule */
+
+ static FT_Error Load_ChainPosRule( TTO_ChainPosRule* cpr,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_UShort* b;
+ FT_UShort* i;
+ FT_UShort* l;
+
+ TTO_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, FT_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, FT_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, FT_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, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( plr );
+
+ Fail2:
+ FREE( l );
+
+ Fail3:
+ FREE( i );
+
+ Fail4:
+ FREE( b );
+ return error;
+ }
+
+
+ static void Free_ChainPosRule( TTO_ChainPosRule* cpr,
+ FT_Memory memory )
+ {
+ FREE( cpr->PosLookupRecord );
+ FREE( cpr->Lookahead );
+ FREE( cpr->Input );
+ FREE( cpr->Backtrack );
+ }
+
+
+ /* ChainPosRuleSet */
+
+ static FT_Error Load_ChainPosRuleSet( TTO_ChainPosRuleSet* cprs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosRule( &cpr[n], memory );
+
+ FREE( cpr );
+ return error;
+ }
+
+
+ static void Free_ChainPosRuleSet( TTO_ChainPosRuleSet* cprs,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ChainPosRule* cpr;
+
+
+ if ( cprs->ChainPosRule )
+ {
+ count = cprs->ChainPosRuleCount;
+ cpr = cprs->ChainPosRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosRule( &cpr[n], memory );
+
+ FREE( cpr );
+ }
+ }
+
+
+ /* ChainContextPosFormat1 */
+
+ static FT_Error Load_ChainContextPos1( TTO_ChainContextPosFormat1* ccpf1,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &ccpf1->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosRuleSet( &cprs[n], memory );
+
+ FREE( cprs );
+
+ Fail2:
+ Free_Coverage( &ccpf1->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_ChainContext1( TTO_ChainContextPosFormat1* ccpf1,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ChainPosRuleSet* cprs;
+
+
+ if ( ccpf1->ChainPosRuleSet )
+ {
+ count = ccpf1->ChainPosRuleSetCount;
+ cprs = ccpf1->ChainPosRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosRuleSet( &cprs[n], memory );
+
+ FREE( cprs );
+ }
+
+ Free_Coverage( &ccpf1->Coverage, memory );
+ }
+
+
+ /* ChainPosClassRule */
+
+ static FT_Error Load_ChainPosClassRule(
+ TTO_ChainContextPosFormat2* ccpf2,
+ TTO_ChainPosClassRule* cpcr,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ FT_UShort* b;
+ FT_UShort* i;
+ FT_UShort* l;
+ TTO_PosLookupRecord* plr;
+ FT_Bool* d;
+
+
+ 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, FT_UShort ) )
+ return error;
+
+ b = cpcr->Backtrack;
+ d = ccpf2->BacktrackClassDef.Defined;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail4;
+
+ for ( n = 0; n < count; n++ )
+ {
+ b[n] = GET_UShort();
+
+ /* We check whether the specific class is used at all. If not,
+ class 0 is used instead. */
+
+ if ( !d[b[n]] )
+ b[n] = 0;
+ }
+
+ 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, FT_UShort ) )
+ goto Fail4;
+
+ i = cpcr->Input;
+ d = ccpf2->InputClassDef.Defined;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail3;
+
+ for ( n = 0; n < count; n++ )
+ {
+ i[n] = GET_UShort();
+
+ if ( !d[i[n]] )
+ i[n] = 0;
+ }
+
+ 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, FT_UShort ) )
+ goto Fail3;
+
+ l = cpcr->Lookahead;
+ d = ccpf2->LookaheadClassDef.Defined;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ {
+ l[n] = GET_UShort();
+
+ if ( !d[l[n]] )
+ l[n] = 0;
+ }
+
+ 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, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( plr );
+
+ Fail2:
+ FREE( l );
+
+ Fail3:
+ FREE( i );
+
+ Fail4:
+ FREE( b );
+ return error;
+ }
+
+
+ static void Free_ChainPosClassRule( TTO_ChainPosClassRule* cpcr,
+ FT_Memory memory )
+ {
+ FREE( cpcr->PosLookupRecord );
+ FREE( cpcr->Lookahead );
+ FREE( cpcr->Input );
+ FREE( cpcr->Backtrack );
+ }
+
+
+ /* PosClassSet */
+
+ static FT_Error Load_ChainPosClassSet(
+ TTO_ChainContextPosFormat2* ccpf2,
+ TTO_ChainPosClassSet* cpcs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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,
+ TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosClassRule( &cpcr[n], memory );
+
+ FREE( cpcr );
+ return error;
+ }
+
+
+ static void Free_ChainPosClassSet( TTO_ChainPosClassSet* cpcs,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ChainPosClassRule* cpcr;
+
+
+ if ( cpcs->ChainPosClassRule )
+ {
+ count = cpcs->ChainPosClassRuleCount;
+ cpcr = cpcs->ChainPosClassRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosClassRule( &cpcr[n], memory );
+
+ FREE( cpcr );
+ }
+ }
+
+
+ /* ChainContextPosFormat2 */
+
+ static FT_Error Load_ChainContextPos2( TTO_ChainContextPosFormat2* ccpf2,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+ FT_ULong backtrack_offset, input_offset, lookahead_offset;
+
+ TTO_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 = Load_Coverage( &ccpf2->Coverage, stream ) ) != TT_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 8L ) )
+ goto Fail5;
+
+ backtrack_offset = GET_UShort() + base_offset;
+ input_offset = GET_UShort() + base_offset;
+ lookahead_offset = GET_UShort() + base_offset;
+
+ /* `ChainPosClassSetCount' is the upper limit for input class values,
+ thus we read it now to make an additional safety check. */
+
+ count = ccpf2->ChainPosClassSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( backtrack_offset ) ||
+ ( error = Load_ClassDefinition( &ccpf2->BacktrackClassDef, count,
+ stream ) ) != TT_Err_Ok )
+ goto Fail5;
+ if ( FILE_Seek( input_offset ) ||
+ ( error = Load_ClassDefinition( &ccpf2->InputClassDef, count,
+ stream ) ) != TT_Err_Ok )
+ goto Fail4;
+ if ( FILE_Seek( lookahead_offset ) ||
+ ( error = Load_ClassDefinition( &ccpf2->LookaheadClassDef, count,
+ stream ) ) != TT_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ ccpf2->ChainPosClassSet = NULL;
+ ccpf2->MaxBacktrackLength = 0;
+ ccpf2->MaxInputLength = 0;
+ ccpf2->MaxLookaheadLength = 0;
+
+ if ( ALLOC_ARRAY( ccpf2->ChainPosClassSet, count, TTO_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 ) ) != TT_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 TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosClassSet( &cpcs[n], memory );
+
+ FREE( cpcs );
+
+ Fail2:
+ Free_ClassDefinition( &ccpf2->LookaheadClassDef, memory );
+
+ Fail3:
+ Free_ClassDefinition( &ccpf2->InputClassDef, memory );
+
+ Fail4:
+ Free_ClassDefinition( &ccpf2->BacktrackClassDef, memory );
+
+ Fail5:
+ Free_Coverage( &ccpf2->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_ChainContext2( TTO_ChainContextPosFormat2* ccpf2,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ChainPosClassSet* cpcs;
+
+
+ if ( ccpf2->ChainPosClassSet )
+ {
+ count = ccpf2->ChainPosClassSetCount;
+ cpcs = ccpf2->ChainPosClassSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainPosClassSet( &cpcs[n], memory );
+
+ FREE( cpcs );
+ }
+
+ Free_ClassDefinition( &ccpf2->LookaheadClassDef, memory );
+ Free_ClassDefinition( &ccpf2->InputClassDef, memory );
+ Free_ClassDefinition( &ccpf2->BacktrackClassDef, memory );
+
+ Free_Coverage( &ccpf2->Coverage, memory );
+ }
+
+
+ /* ChainContextPosFormat3 */
+
+ static FT_Error Load_ChainContextPos3( TTO_ChainContextPosFormat3* ccpf3,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_UShort backtrack_count, input_count, lookahead_count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_Coverage* b;
+ TTO_Coverage* i;
+ TTO_Coverage* l;
+ TTO_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,
+ TTO_Coverage ) )
+ return error;
+
+ b = ccpf3->BacktrackCoverage;
+
+ for ( n = 0; n < backtrack_count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Coverage( &b[n], stream ) ) != TT_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, TTO_Coverage ) )
+ goto Fail4;
+
+ i = ccpf3->InputCoverage;
+
+ for ( n = 0; n < input_count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Coverage( &i[n], stream ) ) != TT_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,
+ TTO_Coverage ) )
+ goto Fail3;
+
+ l = ccpf3->LookaheadCoverage;
+
+ for ( n = 0; n < lookahead_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 = Load_Coverage( &l[n], stream ) ) != TT_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, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( plr );
+
+ Fail2:
+ for ( n = 0; n < lookahead_count; n++ )
+ Free_Coverage( &l[n], memory );
+
+ FREE( l );
+
+ Fail3:
+ for ( n = 0; n < input_count; n++ )
+ Free_Coverage( &i[n], memory );
+
+ FREE( i );
+
+ Fail4:
+ for ( n = 0; n < backtrack_count; n++ )
+ Free_Coverage( &b[n], memory );
+
+ FREE( b );
+ return error;
+ }
+
+
+ static void Free_ChainContext3( TTO_ChainContextPosFormat3* ccpf3,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_Coverage* c;
+
+
+ FREE( ccpf3->PosLookupRecord );
+
+ if ( ccpf3->LookaheadCoverage )
+ {
+ count = ccpf3->LookaheadGlyphCount;
+ c = ccpf3->LookaheadCoverage;
+
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ }
+
+ if ( ccpf3->InputCoverage )
+ {
+ count = ccpf3->InputGlyphCount;
+ c = ccpf3->InputCoverage;
+
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ }
+
+ if ( ccpf3->BacktrackCoverage )
+ {
+ count = ccpf3->BacktrackGlyphCount;
+ c = ccpf3->BacktrackCoverage;
+
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ }
+ }
+
+
+ /* ChainContextPos */
+
+ FT_Error Load_ChainContextPos( TTO_ChainContextPos* ccp,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ 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 TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+ void Free_ChainContextPos( TTO_ChainContextPos* ccp,
+ FT_Memory memory )
+ {
+ switch ( ccp->PosFormat )
+ {
+ case 1:
+ Free_ChainContext1( &ccp->ccpf.ccpf1, memory );
+ break;
+
+ case 2:
+ Free_ChainContext2( &ccp->ccpf.ccpf2, memory );
+ break;
+
+ case 3:
+ Free_ChainContext3( &ccp->ccpf.ccpf3, memory );
+ break;
+ }
+ }
+
+
+ static FT_Error Lookup_ChainContextPos1(
+ GPOS_Instance* gpi,
+ TTO_ChainContextPosFormat1* ccpf1,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, property;
+ FT_UShort i, j, k, num_cpr, curr_pos;
+ FT_UShort bgc, igc, lgc;
+ FT_Error error;
+ FT_UShort* s_in;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_ChainPosRule* cpr;
+ TTO_ChainPosRule curr_cpr;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gpos->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &ccpf1->Coverage, in->string[in->pos], &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 )
+ continue;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+ continue;
+
+ 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 */
+
+ curr_pos = 0;
+ s_in = &in->string[curr_pos];
+
+ for ( i = bgc, j = in->pos - 1; i > 0; i--, j-- )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( j > curr_pos )
+ j--;
+ else
+ break;
+ }
+
+ if ( s_in[j] != curr_cpr.Backtrack[i - 1] )
+ break;
+ }
+
+ if ( i != 0 )
+ continue;
+ }
+
+ curr_pos = in->pos;
+ s_in = &in->string[curr_pos];
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( s_in[j] != curr_cpr.Input[i - 1] )
+ break;
+ }
+
+ if ( i != igc )
+ continue;
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ curr_pos = j;
+ s_in = &in->string[curr_pos];
+
+ for ( i = 0, j = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( s_in[j] != curr_cpr.Lookahead[i] )
+ break;
+ }
+
+ if ( i == lgc )
+ return Do_ContextPos( gpi, igc,
+ curr_cpr.PosCount,
+ curr_cpr.PosLookupRecord,
+ in, out,
+ nesting_level );
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ static FT_Error Lookup_ChainContextPos2(
+ GPOS_Instance* gpi,
+ TTO_ChainContextPosFormat2* ccpf2,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, property;
+ FT_Memory memory = gpi->face->memory;
+ FT_Error error;
+ FT_UShort i, j, k, curr_pos;
+ FT_UShort bgc, igc, lgc;
+ FT_UShort known_backtrack_classes,
+ known_input_classes,
+ known_lookahead_classes;
+
+ FT_UShort* backtrack_classes;
+ FT_UShort* input_classes;
+ FT_UShort* lookahead_classes;
+
+ FT_UShort* s_in;
+
+ FT_UShort* bc;
+ FT_UShort* ic;
+ FT_UShort* lc;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_ChainPosClassSet* cpcs;
+ TTO_ChainPosClassRule cpcr;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gpos->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], 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 = Coverage_Index( &ccpf2->Coverage, in->string[in->pos], &index );
+ if ( error )
+ return error;
+
+ if ( ALLOC_ARRAY( backtrack_classes, ccpf2->MaxBacktrackLength, FT_UShort ) )
+ return error;
+ known_backtrack_classes = 0;
+
+ if ( ALLOC_ARRAY( input_classes, ccpf2->MaxInputLength, FT_UShort ) )
+ goto End3;
+ known_input_classes = 1;
+
+ if ( ALLOC_ARRAY( lookahead_classes, ccpf2->MaxLookaheadLength, FT_UShort ) )
+ goto End2;
+ known_lookahead_classes = 0;
+
+ error = Get_Class( &ccpf2->InputClassDef, in->string[in->pos],
+ &input_classes[0], NULL );
+ if ( error )
+ goto End1;
+
+ cpcs = &ccpf2->ChainPosClassSet[input_classes[0]];
+ if ( !cpcs )
+ {
+ error = TTO_Err_Invalid_GPOS_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 )
+ continue;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+ continue;
+
+ 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. */
+
+ curr_pos = 0;
+ s_in = &in->string[curr_pos];
+ bc = cpcr.Backtrack;
+
+ for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( j > curr_pos )
+ j--;
+ else
+ break;
+ }
+
+ if ( i >= known_backtrack_classes )
+ {
+ /* Keeps us from having to do this for each rule */
+
+ error = Get_Class( &ccpf2->BacktrackClassDef, s_in[j],
+ &backtrack_classes[i], NULL );
+ if ( error && error != TTO_Err_Not_Covered )
+ goto End1;
+ known_backtrack_classes = i;
+ }
+
+ if ( bc[bgc - 1 - i] != backtrack_classes[i] )
+ break;
+ }
+
+ if ( i != bgc )
+ continue;
+ }
+
+ curr_pos = in->pos;
+ s_in = &in->string[curr_pos];
+ ic = cpcr.Input;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ goto End1;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( i >= known_input_classes )
+ {
+ error = Get_Class( &ccpf2->InputClassDef, s_in[j],
+ &input_classes[i], NULL );
+ if ( error && error != TTO_Err_Not_Covered )
+ goto End1;
+ known_input_classes = i;
+ }
+
+ if ( ic[i - 1] != input_classes[i] )
+ break;
+ }
+
+ if ( i != igc )
+ continue;
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ curr_pos = j;
+ s_in = &in->string[curr_pos];
+ lc = cpcr.Lookahead;
+
+ for ( i = 0, j = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( i >= known_lookahead_classes )
+ {
+ error = Get_Class( &ccpf2->LookaheadClassDef, s_in[j],
+ &lookahead_classes[i], NULL );
+ if ( error && error != TTO_Err_Not_Covered )
+ goto End1;
+ known_lookahead_classes = i;
+ }
+
+ if ( lc[i] != lookahead_classes[i] )
+ break;
+ }
+
+ if ( i == lgc )
+ {
+ error = Do_ContextPos( gpi, igc,
+ cpcr.PosCount,
+ cpcr.PosLookupRecord,
+ in, out,
+ nesting_level );
+ goto End1;
+ }
+ }
+
+ error = TTO_Err_Not_Covered;
+
+ End1:
+ FREE( lookahead_classes );
+
+ End2:
+ FREE( input_classes );
+
+ End3:
+ FREE( backtrack_classes );
+ return error;
+ }
+
+
+ static FT_Error Lookup_ChainContextPos3(
+ GPOS_Instance* gpi,
+ TTO_ChainContextPosFormat3* ccpf3,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, i, j, curr_pos, property;
+ FT_UShort bgc, igc, lgc;
+ FT_Error error;
+ FT_UShort* s_in;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ TTO_Coverage* bc;
+ TTO_Coverage* ic;
+ TTO_Coverage* lc;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gpos->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ bgc = ccpf3->BacktrackGlyphCount;
+ igc = ccpf3->InputGlyphCount;
+ lgc = ccpf3->LookaheadGlyphCount;
+
+ if ( context_length != 0xFFFF && context_length < igc )
+ return TTO_Err_Not_Covered;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+ return TTO_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 */
+
+ curr_pos = 0;
+ s_in = &in->string[curr_pos];
+ bc = ccpf3->BacktrackCoverage;
+
+ for ( i = bgc, j = in->pos - 1; i > 0; i--, j-- )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( j > curr_pos )
+ j--;
+ else
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &bc[i - 1], s_in[j], &index );
+ if ( error )
+ return error;
+ }
+ }
+
+ curr_pos = in->pos;
+ s_in = &in->string[curr_pos];
+ ic = ccpf3->InputCoverage;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &ic[i], s_in[j], &index );
+ if ( error )
+ return error;
+ }
+
+ /* we are starting for lookahead glyphs right after the last context
+ glyph */
+
+ curr_pos = j;
+ s_in = &in->string[curr_pos];
+ lc = ccpf3->LookaheadCoverage;
+
+ for ( i = 0, j = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &lc[i], s_in[j], &index );
+ if ( error )
+ return error;
+ }
+
+ return Do_ContextPos( gpi, igc,
+ ccpf3->PosCount,
+ ccpf3->PosLookupRecord,
+ in, out,
+ nesting_level );
+ }
+
+
+ static FT_Error Lookup_ChainContextPos(
+ GPOS_Instance* gpi,
+ TTO_ChainContextPos* ccp,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ switch ( ccp->PosFormat )
+ {
+ case 1:
+ return Lookup_ChainContextPos1( gpi, &ccp->ccpf.ccpf1, in, out,
+ flags, context_length,
+ nesting_level );
+
+ case 2:
+ return Lookup_ChainContextPos2( gpi, &ccp->ccpf.ccpf2, in, out,
+ flags, context_length,
+ nesting_level );
+
+ case 3:
+ return Lookup_ChainContextPos3( gpi, &ccp->ccpf.ccpf3, in, out,
+ flags, context_length,
+ nesting_level );
+
+ default:
+ return TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+
+ /***********
+ * GPOS API
+ ***********/
+
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Select_Script( TTO_GPOSHeader* gpos,
+ FT_ULong script_tag,
+ FT_UShort* script_index )
+ {
+ FT_UShort n;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+
+
+ if ( !gpos || !script_index )
+ return TT_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 TT_Err_Ok;
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Select_Language( TTO_GPOSHeader* gpos,
+ FT_ULong language_tag,
+ FT_UShort script_index,
+ FT_UShort* language_index,
+ FT_UShort* req_feature_index )
+ {
+ FT_UShort n;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+ TTO_Script* s;
+ TTO_LangSysRecord* lsr;
+
+
+ if ( !gpos || !language_index || !req_feature_index )
+ return TT_Err_Invalid_Argument;
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return TT_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 TT_Err_Ok;
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ /* selecting 0xFFFF for language_index asks for the values of the
+ default language (DefaultLangSys) */
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Select_Feature( TTO_GPOSHeader* gpos,
+ FT_ULong feature_tag,
+ FT_UShort script_index,
+ FT_UShort language_index,
+ FT_UShort* feature_index )
+ {
+ FT_UShort n;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+ TTO_Script* s;
+ TTO_LangSysRecord* lsr;
+ TTO_LangSys* ls;
+ FT_UShort* fi;
+
+ TTO_FeatureList* fl;
+ TTO_FeatureRecord* fr;
+
+
+ if ( !gpos || !feature_index )
+ return TT_Err_Invalid_Argument;
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ fl = &gpos->FeatureList;
+ fr = fl->FeatureRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return TT_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 TT_Err_Invalid_Argument;
+
+ ls = &lsr[language_index].LangSys;
+ }
+
+ fi = ls->FeatureIndex;
+
+ for ( n = 0; n < ls->FeatureCount; n++ )
+ {
+ if ( fi[n] >= fl->FeatureCount )
+ return TTO_Err_Invalid_GPOS_SubTable_Format;
+
+ if ( feature_tag == fr[fi[n]].FeatureTag )
+ {
+ *feature_index = fi[n];
+
+ return TT_Err_Ok;
+ }
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ /* The next three functions return a null-terminated list */
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Query_Scripts( TTO_GPOSHeader* gpos,
+ FT_ULong** script_tag_list )
+ {
+ FT_Error error;
+ FT_Memory memory = gpos->memory;
+ FT_UShort n;
+ FT_ULong* stl;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+
+
+ if ( !gpos || !script_tag_list )
+ return TT_Err_Invalid_Argument;
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, FT_ULong ) )
+ return error;
+
+ for ( n = 0; n < sl->ScriptCount; n++ )
+ stl[n] = sr[n].ScriptTag;
+ stl[n] = 0;
+
+ *script_tag_list = stl;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Query_Languages( TTO_GPOSHeader* gpos,
+ FT_UShort script_index,
+ FT_ULong** language_tag_list )
+ {
+ FT_Error error;
+ FT_Memory memory = gpos->memory;
+ FT_UShort n;
+ FT_ULong* ltl;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+ TTO_Script* s;
+ TTO_LangSysRecord* lsr;
+
+
+ if ( !gpos || !language_tag_list )
+ return TT_Err_Invalid_Argument;
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return TT_Err_Invalid_Argument;
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, FT_ULong ) )
+ return error;
+
+ for ( n = 0; n < s->LangSysCount; n++ )
+ ltl[n] = lsr[n].LangSysTag;
+ ltl[n] = 0;
+
+ *language_tag_list = ltl;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* selecting 0xFFFF for language_index asks for the values of the
+ default language (DefaultLangSys) */
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Query_Features( TTO_GPOSHeader* gpos,
+ FT_UShort script_index,
+ FT_UShort language_index,
+ FT_ULong** feature_tag_list )
+ {
+ FT_UShort n;
+ FT_Error error;
+ FT_Memory memory = gpos->memory;
+ FT_ULong* ftl;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+ TTO_Script* s;
+ TTO_LangSysRecord* lsr;
+ TTO_LangSys* ls;
+ FT_UShort* fi;
+
+ TTO_FeatureList* fl;
+ TTO_FeatureRecord* fr;
+
+
+ if ( !gpos || !feature_tag_list )
+ return TT_Err_Invalid_Argument;
+
+ sl = &gpos->ScriptList;
+ sr = sl->ScriptRecord;
+
+ fl = &gpos->FeatureList;
+ fr = fl->FeatureRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return TT_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 TT_Err_Invalid_Argument;
+
+ ls = &lsr[language_index].LangSys;
+ }
+
+ fi = ls->FeatureIndex;
+
+ if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, FT_ULong ) )
+ return error;
+
+ for ( n = 0; n < ls->FeatureCount; n++ )
+ {
+ if ( fi[n] >= fl->FeatureCount )
+ {
+ FREE( ftl );
+ return TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+ ftl[n] = fr[fi[n]].FeatureTag;
+ }
+ ftl[n] = 0;
+
+ *feature_tag_list = ftl;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* Do an individual subtable lookup. Returns TT_Err_Ok if positioning
+ has been done, or TTO_Err_Not_Covered if not. */
+
+ static FT_Error Do_Glyph_Lookup( GPOS_Instance* gpi,
+ FT_UShort lookup_index,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_Error error = TT_Err_Ok;
+ FT_UShort i, flags;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+ TTO_Lookup* lo;
+
+
+ nesting_level++;
+
+ if ( nesting_level > TTO_MAX_NESTING_LEVEL )
+ return TTO_Err_Too_Many_Nested_Contexts;
+
+ lo = &gpos->LookupList.Lookup[lookup_index];
+ flags = lo->LookupFlag;
+
+ for ( i = 0; i < lo->SubTableCount; i++ )
+ {
+ switch ( lo->LookupType )
+ {
+ case GPOS_LOOKUP_SINGLE:
+ error = Lookup_SinglePos( gpi,
+ &lo->SubTable[i].st.gpos.single,
+ in, out,
+ flags, context_length );
+ break;
+
+ case GPOS_LOOKUP_PAIR:
+ error = Lookup_PairPos( gpi,
+ &lo->SubTable[i].st.gpos.pair,
+ in, out,
+ flags, context_length );
+ break;
+
+ case GPOS_LOOKUP_CURSIVE:
+ error = Lookup_CursivePos( gpi,
+ &lo->SubTable[i].st.gpos.cursive,
+ in, out,
+ flags, context_length );
+ break;
+
+ case GPOS_LOOKUP_MARKBASE:
+ error = Lookup_MarkBasePos( gpi,
+ &lo->SubTable[i].st.gpos.markbase,
+ in, out,
+ flags, context_length );
+ break;
+
+ case GPOS_LOOKUP_MARKLIG:
+ error = Lookup_MarkLigPos( gpi,
+ &lo->SubTable[i].st.gpos.marklig,
+ in, out,
+ flags, context_length );
+ break;
+
+ case GPOS_LOOKUP_MARKMARK:
+ error = Lookup_MarkMarkPos( gpi,
+ &lo->SubTable[i].st.gpos.markmark,
+ in, out,
+ flags, context_length );
+ break;
+
+ case GPOS_LOOKUP_CONTEXT:
+ error = Lookup_ContextPos( gpi,
+ &lo->SubTable[i].st.gpos.context,
+ in, out,
+ flags, context_length,
+ nesting_level );
+ break;
+
+ case GPOS_LOOKUP_CHAIN:
+ error = Lookup_ChainContextPos( gpi,
+ &lo->SubTable[i].st.gpos.chain,
+ in, out,
+ flags, context_length,
+ nesting_level );
+ break;
+ }
+
+ /* Check whether we have a successful positioning or an error other
+ than TTO_Err_Not_Covered */
+
+ if ( error != TTO_Err_Not_Covered )
+ return error;
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ /* apply one lookup to the input string object */
+
+ static FT_Error Do_String_Lookup( GPOS_Instance* gpi,
+ FT_UShort lookup_index,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data* out )
+ {
+ FT_Error error = TTO_Err_Not_Covered;
+ TTO_GPOSHeader* gpos = gpi->gpos;
+
+ FT_UShort* properties = gpos->LookupList.Properties;
+ FT_UShort* p_in = in->properties;
+
+ int nesting_level = 0;
+
+
+ gpi->last = 0xFFFF; /* no last valid glyph for cursive pos. */
+
+ in->pos = 0;
+
+ while ( in->pos < in->length )
+ {
+ if ( ~p_in[in->pos] & properties[lookup_index] )
+ {
+ /* 0xFFFF indicates that we don't have a context length yet. */
+
+ /* 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 = Do_Glyph_Lookup( gpi, lookup_index, in, out,
+ 0xFFFF, nesting_level );
+ if ( error && error != TTO_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 = TTO_Err_Not_Covered;
+ }
+
+ if ( error == TTO_Err_Not_Covered )
+ (in->pos)++;
+ }
+
+ return error;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Add_Feature( TTO_GPOSHeader* gpos,
+ FT_UShort feature_index,
+ FT_UShort property )
+ {
+ FT_UShort i;
+
+ TTO_Feature feature;
+ FT_UShort* properties;
+ FT_UShort* index;
+
+
+ if ( !gpos ||
+ feature_index >= gpos->FeatureList.FeatureCount )
+ return TT_Err_Invalid_Argument;
+
+ properties = gpos->LookupList.Properties;
+
+ feature = gpos->FeatureList.FeatureRecord[feature_index].Feature;
+ index = feature.LookupListIndex;
+
+ for ( i = 0; i < feature.LookupListCount; i++ )
+ properties[index[i]] |= property;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Clear_Features( TTO_GPOSHeader* gpos )
+ {
+ FT_UShort i;
+
+ FT_UShort* properties;
+
+
+ if ( !gpos )
+ return TT_Err_Invalid_Argument;
+
+ properties = gpos->LookupList.Properties;
+
+ for ( i = 0; i < gpos->LookupList.LookupCount; i++ )
+ properties[i] = 0;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Register_Glyph_Function( TTO_GPOSHeader* gpos,
+ TTO_GlyphFunction gfunc )
+ {
+ if ( !gpos )
+ return TT_Err_Invalid_Argument;
+
+ gpos->gfunc = gfunc;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Register_MM_Function( TTO_GPOSHeader* gpos,
+ TTO_MMFunction mmfunc,
+ void* data )
+ {
+ if ( !gpos )
+ return TT_Err_Invalid_Argument;
+
+ gpos->mmfunc = mmfunc;
+ gpos->data = data;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* If `dvi' is TRUE, glyph contour points for anchor points and device
+ tables are ignored -- you will get device independent values. */
+
+ EXPORT_FUNC
+ FT_Error TT_GPOS_Apply_String( FT_Face face,
+ TTO_GPOSHeader* gpos,
+ FT_UShort load_flags,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data** out,
+ FT_Bool dvi,
+ FT_Bool r2l )
+ {
+ FT_Memory memory = gpos->memory;
+ FT_Error error = TTO_Err_Not_Covered;
+ GPOS_Instance gpi;
+
+ FT_UShort j;
+
+ FT_UShort* properties;
+
+
+ if ( !face || !gpos ||
+ !in || in->length == 0 || in->pos >= in->length )
+ return TT_Err_Invalid_Argument;
+
+ properties = gpos->LookupList.Properties;
+
+ gpi.face = face;
+ gpi.gpos = gpos;
+ gpi.load_flags = load_flags;
+ gpi.r2l = r2l;
+ gpi.dvi = dvi;
+
+ if ( *out )
+ FREE( *out );
+ if ( ALLOC_ARRAY( *out, in->length, TTO_GPOS_Data ) )
+ return error;
+
+ for ( j = 0; j < gpos->LookupList.LookupCount; j++ )
+ if ( !properties || properties[j] )
+ {
+ error = Do_String_Lookup( &gpi, j, in, *out );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ }
+
+ return error;
+ }
+
+/* END */
diff --git a/src/ftxgpos.h b/src/ftxgpos.h
new file mode 100644
index 00000000..56ffb785
--- /dev/null
+++ b/src/ftxgpos.h
@@ -0,0 +1,858 @@
+/*******************************************************************
+ *
+ * ftxgpos.h
+ *
+ * TrueType Open GPOS table support
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ ******************************************************************/
+
+#ifndef FTXOPEN_H
+#error "Don't include this file! Use ftxopen.h instead."
+#endif
+
+#ifndef FTXGPOS_H
+#define FTXGPOS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TTO_Err_Invalid_GPOS_SubTable_Format 0x1020
+#define TTO_Err_Invalid_GPOS_SubTable 0x1021
+
+
+/* Lookup types for glyph positioning */
+
+#define GPOS_LOOKUP_SINGLE 1
+#define GPOS_LOOKUP_PAIR 2
+#define GPOS_LOOKUP_CURSIVE 3
+#define GPOS_LOOKUP_MARKBASE 4
+#define GPOS_LOOKUP_MARKLIG 5
+#define GPOS_LOOKUP_MARKMARK 6
+#define GPOS_LOOKUP_CONTEXT 7
+#define GPOS_LOOKUP_CHAIN 8
+
+
+ /* A pointer to a function which loads a glyph. Its parameters are
+ the same as in a call to TT_Load_Glyph() -- if no glyph loading
+ function will be registered with TTO_GPOS_Register_Glyph_Function(),
+ TT_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. _glyph can be computed with
+
+ _glyph = HANDLE_Glyph( glyph ) */
+
+ typedef FT_Error (*TTO_GlyphFunction)(FT_Face face,
+ FT_UInt glyphIndex,
+ FT_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 TT_GPOS_Register_MM_Function().
+
+ `metric_value' must be returned as a scaled value (but shouldn't
+ be rounded). */
+
+ typedef FT_Error (*TTO_MMFunction)(FT_Face face,
+ FT_UShort metric_id,
+ FT_Pos* metric_value,
+ void* data );
+
+
+ struct TTO_GPOSHeader_
+ {
+ FT_Memory memory;
+
+ FT_Fixed Version;
+
+ TTO_ScriptList ScriptList;
+ TTO_FeatureList FeatureList;
+ TTO_LookupList LookupList;
+
+ TTO_GDEFHeader* gdef;
+
+ /* the next field is used for a callback function to get the
+ glyph outline. */
+
+ TTO_GlyphFunction gfunc;
+
+ /* this is OpenType 1.2 -- Multiple Master fonts need this
+ callback function to get various metric values from the
+ PostScript interpreter. */
+
+ TTO_MMFunction mmfunc;
+ void* data;
+ };
+
+ typedef struct TTO_GPOSHeader_ TTO_GPOSHeader;
+ typedef struct TTO_GPOSHeader_* TTO_GPOS;
+
+
+ /* shared tables */
+
+ struct TTO_ValueRecord_
+ {
+ FT_Short XPlacement; /* horizontal adjustment for
+ placement */
+ FT_Short YPlacement; /* vertical adjustment for
+ placement */
+ FT_Short XAdvance; /* horizontal adjustment for
+ advance */
+ FT_Short YAdvance; /* vertical adjustment for
+ advance */
+ TTO_Device XPlacementDevice; /* device table for horizontal
+ placement */
+ TTO_Device YPlacementDevice; /* device table for vertical
+ placement */
+ TTO_Device XAdvanceDevice; /* device table for horizontal
+ advance */
+ TTO_Device YAdvanceDevice; /* device table for vertical
+ advance */
+ FT_UShort XIdPlacement; /* horizontal placement metric ID */
+ FT_UShort YIdPlacement; /* vertical placement metric ID */
+ FT_UShort XIdAdvance; /* horizontal advance metric ID */
+ FT_UShort YIdAdvance; /* vertical advance metric ID */
+ };
+
+ typedef struct TTO_ValueRecord_ TTO_ValueRecord;
+
+
+/* Mask values to scan the value format of the ValueRecord structure.
+ We always expand compressed ValueRecords of the font. */
+
+#define HAVE_X_PLACEMENT 0x0001
+#define HAVE_Y_PLACEMENT 0x0002
+#define HAVE_X_ADVANCE 0x0004
+#define HAVE_Y_ADVANCE 0x0008
+#define HAVE_X_PLACEMENT_DEVICE 0x0010
+#define HAVE_Y_PLACEMENT_DEVICE 0x0020
+#define HAVE_X_ADVANCE_DEVICE 0x0040
+#define HAVE_Y_ADVANCE_DEVICE 0x0080
+#define HAVE_X_ID_PLACEMENT 0x0100
+#define HAVE_Y_ID_PLACEMENT 0x0200
+#define HAVE_X_ID_ADVANCE 0x0400
+#define HAVE_Y_ID_ADVANCE 0x0800
+
+
+ struct TTO_AnchorFormat1_
+ {
+ FT_Short XCoordinate; /* horizontal value */
+ FT_Short YCoordinate; /* vertical value */
+ };
+
+ typedef struct TTO_AnchorFormat1_ TTO_AnchorFormat1;
+
+
+ struct TTO_AnchorFormat2_
+ {
+ FT_Short XCoordinate; /* horizontal value */
+ FT_Short YCoordinate; /* vertical value */
+ FT_UShort AnchorPoint; /* index to glyph contour point */
+ };
+
+ typedef struct TTO_AnchorFormat2_ TTO_AnchorFormat2;
+
+
+ struct TTO_AnchorFormat3_
+ {
+ FT_Short XCoordinate; /* horizontal value */
+ FT_Short YCoordinate; /* vertical value */
+ TTO_Device XDeviceTable; /* device table for X coordinate */
+ TTO_Device YDeviceTable; /* device table for Y coordinate */
+ };
+
+ typedef struct TTO_AnchorFormat3_ TTO_AnchorFormat3;
+
+
+ struct TTO_AnchorFormat4_
+ {
+ FT_UShort XIdAnchor; /* horizontal metric ID */
+ FT_UShort YIdAnchor; /* vertical metric ID */
+ };
+
+ typedef struct TTO_AnchorFormat4_ TTO_AnchorFormat4;
+
+
+ struct TTO_Anchor_
+ {
+ FT_UShort PosFormat; /* 1, 2, 3, or 4 -- 0 indicates
+ that there is no Anchor table */
+
+ union
+ {
+ TTO_AnchorFormat1 af1;
+ TTO_AnchorFormat2 af2;
+ TTO_AnchorFormat3 af3;
+ TTO_AnchorFormat4 af4;
+ } af;
+ };
+
+ typedef struct TTO_Anchor_ TTO_Anchor;
+
+
+ struct TTO_MarkRecord_
+ {
+ FT_UShort Class; /* mark class */
+ TTO_Anchor MarkAnchor; /* anchor table */
+ };
+
+ typedef struct TTO_MarkRecord_ TTO_MarkRecord;
+
+
+ struct TTO_MarkArray_
+ {
+ FT_UShort MarkCount; /* number of MarkRecord tables */
+ TTO_MarkRecord* MarkRecord; /* array of MarkRecord tables */
+ };
+
+ typedef struct TTO_MarkArray_ TTO_MarkArray;
+
+
+ /* LookupType 1 */
+
+ struct TTO_SinglePosFormat1_
+ {
+ TTO_ValueRecord Value; /* ValueRecord for all covered
+ glyphs */
+ };
+
+ typedef struct TTO_SinglePosFormat1_ TTO_SinglePosFormat1;
+
+
+ struct TTO_SinglePosFormat2_
+ {
+ FT_UShort ValueCount; /* number of ValueRecord tables */
+ TTO_ValueRecord* Value; /* array of ValueRecord tables */
+ };
+
+ typedef struct TTO_SinglePosFormat2_ TTO_SinglePosFormat2;
+
+
+ struct TTO_SinglePos_
+ {
+ FT_UShort PosFormat; /* 1 or 2 */
+ TTO_Coverage Coverage; /* Coverage table */
+
+ FT_UShort ValueFormat; /* format of ValueRecord table */
+
+ union
+ {
+ TTO_SinglePosFormat1 spf1;
+ TTO_SinglePosFormat2 spf2;
+ } spf;
+ };
+
+ typedef struct TTO_SinglePos_ TTO_SinglePos;
+
+
+ /* LookupType 2 */
+
+ struct TTO_PairValueRecord_
+ {
+ FT_UShort SecondGlyph; /* glyph ID for second glyph */
+ TTO_ValueRecord Value1; /* pos. data for first glyph */
+ TTO_ValueRecord Value2; /* pos. data for second glyph */
+ };
+
+ typedef struct TTO_PairValueRecord_ TTO_PairValueRecord;
+
+
+ struct TTO_PairSet_
+ {
+ FT_UShort PairValueCount;
+ /* number of PairValueRecord tables */
+ TTO_PairValueRecord* PairValueRecord;
+ /* array of PairValueRecord tables */
+ };
+
+ typedef struct TTO_PairSet_ TTO_PairSet;
+
+
+ struct TTO_PairPosFormat1_
+ {
+ FT_UShort PairSetCount; /* number of PairSet tables */
+ TTO_PairSet* PairSet; /* array of PairSet tables */
+ };
+
+ typedef struct TTO_PairPosFormat1_ TTO_PairPosFormat1;
+
+
+ struct TTO_Class2Record_
+ {
+ TTO_ValueRecord Value1; /* pos. data for first glyph */
+ TTO_ValueRecord Value2; /* pos. data for second glyph */
+ };
+
+ typedef struct TTO_Class2Record_ TTO_Class2Record;
+
+
+ struct TTO_Class1Record_
+ {
+ TTO_Class2Record* Class2Record; /* array of Class2Record tables */
+ };
+
+ typedef struct TTO_Class1Record_ TTO_Class1Record;
+
+
+ struct TTO_PairPosFormat2_
+ {
+ TTO_ClassDefinition ClassDef1; /* class def. for first glyph */
+ TTO_ClassDefinition ClassDef2; /* class def. for second glyph */
+ FT_UShort Class1Count; /* number of classes in ClassDef1
+ table */
+ FT_UShort Class2Count; /* number of classes in ClassDef2
+ table */
+ TTO_Class1Record* Class1Record; /* array of Class1Record tables */
+ };
+
+ typedef struct TTO_PairPosFormat2_ TTO_PairPosFormat2;
+
+
+ struct TTO_PairPos_
+ {
+ FT_UShort PosFormat; /* 1 or 2 */
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort ValueFormat1; /* format of ValueRecord table
+ for first glyph */
+ FT_UShort ValueFormat2; /* format of ValueRecord table
+ for second glyph */
+
+ union
+ {
+ TTO_PairPosFormat1 ppf1;
+ TTO_PairPosFormat2 ppf2;
+ } ppf;
+ };
+
+ typedef struct TTO_PairPos_ TTO_PairPos;
+
+
+ /* LookupType 3 */
+
+ struct TTO_EntryExitRecord_
+ {
+ TTO_Anchor EntryAnchor; /* entry Anchor table */
+ TTO_Anchor ExitAnchor; /* exit Anchor table */
+ };
+
+
+ typedef struct TTO_EntryExitRecord_ TTO_EntryExitRecord;
+
+ struct TTO_CursivePos_
+ {
+ FT_UShort PosFormat; /* always 1 */
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort EntryExitCount;
+ /* number of EntryExitRecord tables */
+ TTO_EntryExitRecord* EntryExitRecord;
+ /* array of EntryExitRecord tables */
+ };
+
+ typedef struct TTO_CursivePos_ TTO_CursivePos;
+
+
+ /* LookupType 4 */
+
+ struct TTO_BaseRecord_
+ {
+ TTO_Anchor* BaseAnchor; /* array of base glyph anchor
+ tables */
+ };
+
+ typedef struct TTO_BaseRecord_ TTO_BaseRecord;
+
+
+ struct TTO_BaseArray_
+ {
+ FT_UShort BaseCount; /* number of BaseRecord tables */
+ TTO_BaseRecord* BaseRecord; /* array of BaseRecord tables */
+ };
+
+ typedef struct TTO_BaseArray_ TTO_BaseArray;
+
+
+ struct TTO_MarkBasePos_
+ {
+ FT_UShort PosFormat; /* always 1 */
+ TTO_Coverage MarkCoverage; /* mark glyph coverage table */
+ TTO_Coverage BaseCoverage; /* base glyph coverage table */
+ FT_UShort ClassCount; /* number of mark classes */
+ TTO_MarkArray MarkArray; /* mark array table */
+ TTO_BaseArray BaseArray; /* base array table */
+ };
+
+ typedef struct TTO_MarkBasePos_ TTO_MarkBasePos;
+
+
+ /* LookupType 5 */
+
+ struct TTO_ComponentRecord_
+ {
+ TTO_Anchor* LigatureAnchor; /* array of ligature glyph anchor
+ tables */
+ };
+
+ typedef struct TTO_ComponentRecord_ TTO_ComponentRecord;
+
+
+ struct TTO_LigatureAttach_
+ {
+ FT_UShort ComponentCount;
+ /* number of ComponentRecord tables */
+ TTO_ComponentRecord* ComponentRecord;
+ /* array of ComponentRecord tables */
+ };
+
+ typedef struct TTO_LigatureAttach_ TTO_LigatureAttach;
+
+
+ struct TTO_LigatureArray_
+ {
+ FT_UShort LigatureCount; /* number of LigatureAttach tables */
+ TTO_LigatureAttach* LigatureAttach;
+ /* array of LigatureAttach tables */
+ };
+
+ typedef struct TTO_LigatureArray_ TTO_LigatureArray;
+
+
+ struct TTO_MarkLigPos_
+ {
+ FT_UShort PosFormat; /* always 1 */
+ TTO_Coverage MarkCoverage; /* mark glyph coverage table */
+ TTO_Coverage LigatureCoverage;
+ /* ligature glyph coverage table */
+ FT_UShort ClassCount; /* number of mark classes */
+ TTO_MarkArray MarkArray; /* mark array table */
+ TTO_LigatureArray LigatureArray; /* ligature array table */
+ };
+
+ typedef struct TTO_MarkLigPos_ TTO_MarkLigPos;
+
+
+ /* LookupType 6 */
+
+ struct TTO_Mark2Record_
+ {
+ TTO_Anchor* Mark2Anchor; /* array of mark glyph anchor
+ tables */
+ };
+
+ typedef struct TTO_Mark2Record_ TTO_Mark2Record;
+
+
+ struct TTO_Mark2Array_
+ {
+ FT_UShort Mark2Count; /* number of Mark2Record tables */
+ TTO_Mark2Record* Mark2Record; /* array of Mark2Record tables */
+ };
+
+ typedef struct TTO_Mark2Array_ TTO_Mark2Array;
+
+
+ struct TTO_MarkMarkPos_
+ {
+ FT_UShort PosFormat; /* always 1 */
+ TTO_Coverage Mark1Coverage; /* first mark glyph coverage table */
+ TTO_Coverage Mark2Coverage; /* second mark glyph coverave table */
+ FT_UShort ClassCount; /* number of combining mark classes */
+ TTO_MarkArray Mark1Array; /* MarkArray table for first mark */
+ TTO_Mark2Array Mark2Array; /* MarkArray table for second mark */
+ };
+
+ typedef struct TTO_MarkMarkPos_ TTO_MarkMarkPos;
+
+
+ /* needed by both lookup type 7 and 8 */
+
+ struct TTO_PosLookupRecord_
+ {
+ FT_UShort SequenceIndex; /* index into current
+ glyph sequence */
+ FT_UShort LookupListIndex; /* Lookup to apply to that pos. */
+ };
+
+ typedef struct TTO_PosLookupRecord_ TTO_PosLookupRecord;
+
+
+ /* LookupType 7 */
+
+ struct TTO_PosRule_
+ {
+ FT_UShort GlyphCount; /* total number of input glyphs */
+ FT_UShort PosCount; /* number of PosLookupRecord tables */
+ FT_UShort* Input; /* array of input glyph IDs */
+ TTO_PosLookupRecord* PosLookupRecord;
+ /* array of PosLookupRecord tables */
+ };
+
+ typedef struct TTO_PosRule_ TTO_PosRule;
+
+
+ struct TTO_PosRuleSet_
+ {
+ FT_UShort PosRuleCount; /* number of PosRule tables */
+ TTO_PosRule* PosRule; /* array of PosRule tables */
+ };
+
+ typedef struct TTO_PosRuleSet_ TTO_PosRuleSet;
+
+
+ struct TTO_ContextPosFormat1_
+ {
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort PosRuleSetCount; /* number of PosRuleSet tables */
+ TTO_PosRuleSet* PosRuleSet; /* array of PosRuleSet tables */
+ };
+
+ typedef struct TTO_ContextPosFormat1_ TTO_ContextPosFormat1;
+
+
+ struct TTO_PosClassRule_
+ {
+ FT_UShort GlyphCount; /* total number of context classes */
+ FT_UShort PosCount; /* number of PosLookupRecord tables */
+ FT_UShort* Class; /* array of classes */
+ TTO_PosLookupRecord* PosLookupRecord;
+ /* array of PosLookupRecord tables */
+ };
+
+ typedef struct TTO_PosClassRule_ TTO_PosClassRule;
+
+
+ struct TTO_PosClassSet_
+ {
+ FT_UShort PosClassRuleCount;
+ /* number of PosClassRule tables */
+ TTO_PosClassRule* PosClassRule; /* array of PosClassRule tables */
+ };
+
+ typedef struct TTO_PosClassSet_ TTO_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 TTO_ContextPosFormat2_
+ {
+ FT_UShort MaxContextLength;
+ /* maximal context length */
+ TTO_Coverage Coverage; /* Coverage table */
+ TTO_ClassDefinition ClassDef; /* ClassDef table */
+ FT_UShort PosClassSetCount;
+ /* number of PosClassSet tables */
+ TTO_PosClassSet* PosClassSet; /* array of PosClassSet tables */
+ };
+
+ typedef struct TTO_ContextPosFormat2_ TTO_ContextPosFormat2;
+
+
+ struct TTO_ContextPosFormat3_
+ {
+ FT_UShort GlyphCount; /* number of input glyphs */
+ FT_UShort PosCount; /* number of PosLookupRecord tables */
+ TTO_Coverage* Coverage; /* array of Coverage tables */
+ TTO_PosLookupRecord* PosLookupRecord;
+ /* array of PosLookupRecord tables */
+ };
+
+ typedef struct TTO_ContextPosFormat3_ TTO_ContextPosFormat3;
+
+
+ struct TTO_ContextPos_
+ {
+ FT_UShort PosFormat; /* 1, 2, or 3 */
+
+ union
+ {
+ TTO_ContextPosFormat1 cpf1;
+ TTO_ContextPosFormat2 cpf2;
+ TTO_ContextPosFormat3 cpf3;
+ } cpf;
+ };
+
+ typedef struct TTO_ContextPos_ TTO_ContextPos;
+
+
+ /* LookupType 8 */
+
+ struct TTO_ChainPosRule_
+ {
+ FT_UShort BacktrackGlyphCount;
+ /* total number of backtrack glyphs */
+ FT_UShort* Backtrack; /* array of backtrack glyph IDs */
+ FT_UShort InputGlyphCount;
+ /* total number of input glyphs */
+ FT_UShort* Input; /* array of input glyph IDs */
+ FT_UShort LookaheadGlyphCount;
+ /* total number of lookahead glyphs */
+ FT_UShort* Lookahead; /* array of lookahead glyph IDs */
+ FT_UShort PosCount; /* number of PosLookupRecords */
+ TTO_PosLookupRecord* PosLookupRecord;
+ /* array of PosLookupRecords */
+ };
+
+ typedef struct TTO_ChainPosRule_ TTO_ChainPosRule;
+
+
+ struct TTO_ChainPosRuleSet_
+ {
+ FT_UShort ChainPosRuleCount;
+ /* number of ChainPosRule tables */
+ TTO_ChainPosRule* ChainPosRule; /* array of ChainPosRule tables */
+ };
+
+ typedef struct TTO_ChainPosRuleSet_ TTO_ChainPosRuleSet;
+
+
+ struct TTO_ChainContextPosFormat1_
+ {
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort ChainPosRuleSetCount;
+ /* number of ChainPosRuleSet tables */
+ TTO_ChainPosRuleSet* ChainPosRuleSet;
+ /* array of ChainPosRuleSet tables */
+ };
+
+ typedef struct TTO_ChainContextPosFormat1_ TTO_ChainContextPosFormat1;
+
+
+ struct TTO_ChainPosClassRule_
+ {
+ FT_UShort BacktrackGlyphCount;
+ /* total number of backtrack
+ classes */
+ FT_UShort* Backtrack; /* array of backtrack classes */
+ FT_UShort InputGlyphCount;
+ /* total number of context classes */
+ FT_UShort* Input; /* array of context classes */
+ FT_UShort LookaheadGlyphCount;
+ /* total number of lookahead
+ classes */
+ FT_UShort* Lookahead; /* array of lookahead classes */
+ FT_UShort PosCount; /* number of PosLookupRecords */
+ TTO_PosLookupRecord* PosLookupRecord;
+ /* array of substitution lookups */
+ };
+
+ typedef struct TTO_ChainPosClassRule_ TTO_ChainPosClassRule;
+
+
+ struct TTO_ChainPosClassSet_
+ {
+ FT_UShort ChainPosClassRuleCount;
+ /* number of ChainPosClassRule
+ tables */
+ TTO_ChainPosClassRule* ChainPosClassRule;
+ /* array of ChainPosClassRule
+ tables */
+ };
+
+ typedef struct TTO_ChainPosClassSet_ TTO_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 TTO_ChainContextPosFormat2_
+ {
+ TTO_Coverage Coverage; /* Coverage table */
+
+ FT_UShort MaxBacktrackLength;
+ /* maximal backtrack length */
+ TTO_ClassDefinition BacktrackClassDef;
+ /* BacktrackClassDef table */
+ FT_UShort MaxInputLength;
+ /* maximal input length */
+ TTO_ClassDefinition InputClassDef;
+ /* InputClassDef table */
+ FT_UShort MaxLookaheadLength;
+ /* maximal lookahead length */
+ TTO_ClassDefinition LookaheadClassDef;
+ /* LookaheadClassDef table */
+
+ FT_UShort ChainPosClassSetCount;
+ /* number of ChainPosClassSet
+ tables */
+ TTO_ChainPosClassSet* ChainPosClassSet;
+ /* array of ChainPosClassSet
+ tables */
+ };
+
+ typedef struct TTO_ChainContextPosFormat2_ TTO_ChainContextPosFormat2;
+
+
+ struct TTO_ChainContextPosFormat3_
+ {
+ FT_UShort BacktrackGlyphCount;
+ /* number of backtrack glyphs */
+ TTO_Coverage* BacktrackCoverage;
+ /* array of backtrack Coverage
+ tables */
+ FT_UShort InputGlyphCount;
+ /* number of input glyphs */
+ TTO_Coverage* InputCoverage;
+ /* array of input coverage
+ tables */
+ FT_UShort LookaheadGlyphCount;
+ /* number of lookahead glyphs */
+ TTO_Coverage* LookaheadCoverage;
+ /* array of lookahead coverage
+ tables */
+ FT_UShort PosCount; /* number of PosLookupRecords */
+ TTO_PosLookupRecord* PosLookupRecord;
+ /* array of substitution lookups */
+ };
+
+ typedef struct TTO_ChainContextPosFormat3_ TTO_ChainContextPosFormat3;
+
+
+ struct TTO_ChainContextPos_
+ {
+ FT_UShort PosFormat; /* 1, 2, or 3 */
+
+ union
+ {
+ TTO_ChainContextPosFormat1 ccpf1;
+ TTO_ChainContextPosFormat2 ccpf2;
+ TTO_ChainContextPosFormat3 ccpf3;
+ } ccpf;
+ };
+
+ typedef struct TTO_ChainContextPos_ TTO_ChainContextPos;
+
+
+ union TTO_GPOS_SubTable_
+ {
+ TTO_SinglePos single;
+ TTO_PairPos pair;
+ TTO_CursivePos cursive;
+ TTO_MarkBasePos markbase;
+ TTO_MarkLigPos marklig;
+ TTO_MarkMarkPos markmark;
+ TTO_ContextPos context;
+ TTO_ChainContextPos chain;
+ };
+
+ typedef union TTO_GPOS_SubTable_ TTO_GPOS_SubTable;
+
+
+ /* This `string object' is much simpler compared to TTO_GSUB_String.
+ A call to TTO_GPOS_Apply_String() will allocate it. */
+
+ struct TTO_GPOS_Data_
+ {
+ FT_Pos x_pos;
+ FT_Pos y_pos;
+ FT_Pos x_advance;
+ FT_Pos y_advance;
+ FT_UShort back; /* number of glyphs to go back
+ for drawing current glyph */
+ FT_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. */
+ };
+
+ typedef struct TTO_GPOS_Data_ TTO_GPOS_Data;
+
+
+ /* finally, the GPOS API */
+
+ /* EXPORT_DEF
+ FT_Export ( FT_Error ) TT_Init_GPOS_Extension( TT_Engine engine ); */
+
+ EXPORT_DEF
+ FT_Error TT_Load_GPOS_Table( FT_Face face,
+ TTO_GPOSHeader** gpos,
+ TTO_GDEFHeader* gdef );
+
+ EXPORT_DEF
+ FT_Error TT_Done_GPOS_Table( TTO_GPOSHeader* gpos );
+
+ EXPORT_DEF
+ FT_Error TT_GPOS_Select_Script( TTO_GPOSHeader* gpos,
+ FT_ULong script_tag,
+ FT_UShort* script_index );
+ EXPORT_DEF
+ FT_Error TT_GPOS_Select_Language( TTO_GPOSHeader* gpos,
+ FT_ULong language_tag,
+ FT_UShort script_index,
+ FT_UShort* language_index,
+ FT_UShort* req_feature_index );
+ EXPORT_DEF
+ FT_Error TT_GPOS_Select_Feature( TTO_GPOSHeader* gpos,
+ FT_ULong feature_tag,
+ FT_UShort script_index,
+ FT_UShort language_index,
+ FT_UShort* feature_index );
+
+ EXPORT_DEF
+ FT_Error TT_GPOS_Query_Scripts( TTO_GPOSHeader* gpos,
+ FT_ULong** script_tag_list );
+ EXPORT_DEF
+ FT_Error TT_GPOS_Query_Languages( TTO_GPOSHeader* gpos,
+ FT_UShort script_index,
+ FT_ULong** language_tag_list );
+ EXPORT_DEF
+ FT_Error TT_GPOS_Query_Features( TTO_GPOSHeader* gpos,
+ FT_UShort script_index,
+ FT_UShort language_index,
+ FT_ULong** feature_tag_list );
+
+ EXPORT_DEF
+ FT_Error TT_GPOS_Add_Feature( TTO_GPOSHeader* gpos,
+ FT_UShort feature_index,
+ FT_UShort property );
+ EXPORT_DEF
+ FT_Error TT_GPOS_Clear_Features( TTO_GPOSHeader* gpos );
+
+ EXPORT_DEF
+ FT_Error TT_GPOS_Register_Glyph_Function( TTO_GPOSHeader* gpos,
+ TTO_GlyphFunction gfunc );
+
+ EXPORT_DEF
+ FT_Error TT_GPOS_Register_MM_Function( TTO_GPOSHeader* gpos,
+ TTO_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. */
+
+ EXPORT_DEF
+ FT_Error TT_GPOS_Apply_String( FT_Face face,
+ TTO_GPOSHeader* gpos,
+ FT_UShort load_flags,
+ TTO_GSUB_String* in,
+ TTO_GPOS_Data** out,
+ FT_Bool dvi,
+ FT_Bool r2l );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FTXGPOS_H */
+
+
+/* END */
diff --git a/src/ftxgsub.c b/src/ftxgsub.c
new file mode 100644
index 00000000..43adb2ce
--- /dev/null
+++ b/src/ftxgsub.c
@@ -0,0 +1,4531 @@
+/*******************************************************************
+ *
+ * ftxgsub.c
+ *
+ * TrueType Open GSUB table support.
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ ******************************************************************/
+
+/* XXX There is *a lot* of duplicated code (cf. formats 5 and 6), but
+ I don't care currently. I believe that it would be possible to
+ save about 50% of TTO code by carefully designing the structures,
+ sharing as much as possible with extensive use of macros. This
+ is something for a volunteer :-) */
+
+#define EXPORT_FUNC
+
+#include <freetype/tttags.h>
+
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/ftmemory.h>
+#include <freetype/internal/tterrors.h>
+#include <freetype/internal/tttypes.h>
+
+#include "ftxopen.h"
+#include "ftxopenf.h"
+
+
+
+#define GSUB_ID Build_Extension_ID( 'G', 'S', 'U', 'B' )
+
+
+#define ADD_String( in, num_in, out, num_out, glyph_data, component, ligID ) \
+ ( ( error = TT_GSUB_Add_String( (in), (num_in), \
+ (out), (num_out), \
+ (glyph_data), (component), (ligID) \
+ ) ) != TT_Err_Ok )
+
+
+ static FT_Error Do_Glyph_Lookup( TTO_GSUBHeader* gsub,
+ FT_UShort lookup_index,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort context_length,
+ int nesting_level );
+
+
+
+ /**********************
+ * Auxiliary functions
+ **********************/
+
+
+ /* The following function copies `num_out' elements from `glyph_data'
+ to `out', advancing the array pointer in the `in' structure by
+ `num_in' elements, and in `out' by `num_out' elements. If the
+ string (resp. the properties) array in `out' is empty or too
+ small, it allocates resp. reallocates the string (and properties)
+ array. Finally, it sets the `length' field of `out' equal to
+ `pos' of the `out' structure.
+
+ If `component' is 0xFFFF, the value `in->component[in->pos]'
+ will be copied `num_out' times, otherwise `component' itself will
+ be used to fill `out->component'.
+
+ If `ligID' is 0xFFFF, the value `in->lig_IDs[in->pos]' will be
+ copied `num_out' times, otherwise `ligID' itself will be used to
+ fill `out->ligIDs'.
+
+ The properties (if defined) for all replaced glyphs are taken
+ from the glyph at position `in->pos'.
+
+ The logClusters[] value for the glyph at position in->pos is used
+ for all replacement glyphs */
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Add_String( TTO_GSUB_String* in,
+ FT_UShort num_in,
+ TTO_GSUB_String* out,
+ FT_UShort num_out,
+ FT_UShort* glyph_data,
+ FT_UShort component,
+ FT_UShort ligID )
+ {
+ FT_Memory memory = in->memory;
+ FT_Error error;
+ FT_UShort i;
+ FT_UShort p_in;
+ FT_UShort*p_out;
+
+
+ /* sanity check */
+
+ if ( !in || !out ||
+ in->length == 0 || in->pos >= in->length ||
+ in->length < in->pos + num_in )
+ return TT_Err_Invalid_Argument;
+
+ if ( out->pos + num_out >= out->allocated )
+ {
+ FT_ULong size = out->pos + num_out + 256L;
+
+
+ /* The following works because all fields in `out' must be
+ initialized to zero (including the `string' field) for the
+ first use. */
+
+ if ( REALLOC_ARRAY( out->string, out->allocated, size, FT_UShort ) )
+ return error;
+ if ( REALLOC_ARRAY( out->components, out->allocated, size, FT_UShort ) )
+ return error;
+ if ( REALLOC_ARRAY( out->ligIDs, out->allocated, size, FT_UShort ) )
+ return error;
+ if ( in->properties )
+ if ( REALLOC_ARRAY( out->properties, out->allocated, size, FT_UShort ) )
+ return error;
+ if ( REALLOC_ARRAY( out->logClusters, out->allocated, size, FT_Int ) )
+ return error;
+
+ out->allocated = size;
+ }
+
+ if ( num_out )
+ {
+ MEM_Copy( &out->string[out->pos], glyph_data,
+ num_out * sizeof ( FT_UShort ) );
+
+ if ( component == 0xFFFF )
+ component = in->components[in->pos];
+
+ p_out = out->components;
+
+ for ( i = out->pos; i < out->pos + num_out; i++ )
+ p_out[i] = component;
+
+ p_out = out->ligIDs;
+
+ if ( ligID == 0xFFFF )
+ ligID = in->ligIDs[in->pos];
+
+ for ( i = out->pos; i < out->pos + num_out; i++ )
+ p_out[i] = ligID;
+
+ if ( in->properties )
+ {
+ p_in = in->properties[in->pos];
+ p_out = out->properties;
+
+ for ( i = out->pos; i < out->pos + num_out; i++ )
+ p_out[i] = p_in;
+ }
+
+ for ( i = out->pos; i < out->pos + num_out; i++ )
+ out->logClusters[i] = in->logClusters[in->pos];
+ }
+
+ in->pos += num_in;
+ out->pos += num_out;
+
+ out->length = out->pos;
+
+ return TT_Err_Ok;
+ }
+
+
+#if 0
+
+ /**********************
+ * Extension Functions
+ **********************/
+
+
+ static FT_Error GSUB_Create( void* ext,
+ PFace face )
+ {
+ DEFINE_LOAD_LOCALS( face->stream );
+
+ TTO_GSUBHeader* gsub = (TTO_GSUBHeader*)ext;
+ Long table;
+
+
+ /* by convention */
+
+ if ( !gsub )
+ return TT_Err_Ok;
+
+ /* a null offset indicates that there is no GSUB table */
+
+ gsub->offset = 0;
+
+ /* we store the start offset and the size of the subtable */
+
+ table = TT_LookUp_Table( face, TTAG_GSUB );
+ if ( table < 0 )
+ return TT_Err_Ok; /* The table is optional */
+
+ if ( FILE_Seek( face->dirTables[table].Offset ) ||
+ ACCESS_Frame( 4L ) )
+ return error;
+
+ gsub->offset = FILE_Pos() - 4L; /* undo ACCESS_Frame() */
+ gsub->Version = GET_ULong();
+
+ FORGET_Frame();
+
+ gsub->loaded = FALSE;
+
+ return TT_Err_Ok;
+ }
+
+
+ static FT_Error GSUB_Destroy( void* ext,
+ PFace face )
+ {
+ TTO_GSUBHeader* gsub = (TTO_GSUBHeader*)ext;
+
+
+ /* by convention */
+
+ if ( !gsub )
+ return TT_Err_Ok;
+
+ if ( gsub->loaded )
+ {
+ Free_LookupList( &gsub->LookupList, GSUB, memory );
+ Free_FeatureList( &gsub->FeatureList, memory );
+ Free_ScriptList( &gsub->ScriptList, memory );
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_Init_GSUB_Extension( TT_Engine engine )
+ {
+ PEngine_Instance _engine = HANDLE_Engine( engine );
+
+
+ if ( !_engine )
+ return TT_Err_Invalid_Engine;
+
+ return TT_Register_Extension( _engine,
+ GSUB_ID,
+ sizeof ( TTO_GSUBHeader ),
+ GSUB_Create,
+ GSUB_Destroy );
+ }
+#endif
+
+ EXPORT_FUNC
+ FT_Error TT_Load_GSUB_Table( FT_Face face,
+ TTO_GSUBHeader** retptr,
+ TTO_GDEFHeader* gdef )
+ {
+ FT_Stream stream = face->stream;
+ FT_Memory memory = face->memory;
+ FT_Error error;
+ FT_ULong cur_offset, new_offset, base_offset;
+ TT_Face tt_face = (TT_Face)face;
+
+ FT_UShort i, num_lookups;
+ TTO_GSUBHeader* gsub;
+ TTO_Lookup* lo;
+
+ if ( !retptr )
+ return TT_Err_Invalid_Argument;
+
+ if (( error = tt_face->goto_table( tt_face, TTAG_GSUB, stream, 0 ) ))
+ return error;
+
+ base_offset = FILE_Pos();
+
+ if ( ALLOC ( gsub, sizeof( *gsub ) ) )
+ return error;
+
+ gsub->memory = memory;
+
+ /* 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 = Load_ScriptList( &gsub->ScriptList,
+ stream ) ) != TT_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 = Load_FeatureList( &gsub->FeatureList,
+ stream ) ) != TT_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 = Load_LookupList( &gsub->LookupList,
+ stream, GSUB ) ) != TT_Err_Ok )
+ goto Fail2;
+
+ gsub->gdef = gdef; /* can be NULL */
+
+ /* 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 )
+ {
+ lo = gsub->LookupList.Lookup;
+ num_lookups = gsub->LookupList.LookupCount;
+
+ for ( i = 0; i < num_lookups; i++ )
+ {
+
+ if ( lo[i].LookupFlag & IGNORE_SPECIAL_MARKS )
+ {
+ if ( FILE_Seek( gdef->MarkAttachClassDef_offset ) ||
+ ACCESS_Frame( 2L ) )
+ goto Fail1;
+
+ new_offset = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( !new_offset )
+ return TTO_Err_Invalid_GDEF_SubTable;
+
+ new_offset += base_offset;
+
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_ClassDefinition( &gdef->MarkAttachClassDef,
+ 256, stream ) ) != TT_Err_Ok )
+ goto Fail1;
+
+ break;
+ }
+ }
+ }
+
+ *retptr = gsub;
+
+ return TT_Err_Ok;
+
+ Fail1:
+ Free_LookupList( &gsub->LookupList, GSUB, memory );
+
+ Fail2:
+ Free_FeatureList( &gsub->FeatureList, memory );
+
+ Fail3:
+ Free_ScriptList( &gsub->ScriptList, memory );
+
+ Fail4:
+ FREE ( gsub );
+
+
+ return error;
+ }
+
+ EXPORT_FUNC
+ FT_Error TT_Done_GSUB_Table( TTO_GSUBHeader* gsub )
+ {
+ FT_Memory memory = gsub->memory;
+
+ Free_LookupList( &gsub->LookupList, GSUB, memory );
+ Free_FeatureList( &gsub->FeatureList, memory );
+ Free_ScriptList( &gsub->ScriptList, memory );
+
+ FREE( gsub );
+
+ return TT_Err_Ok;
+ }
+
+ /*****************************
+ * SubTable related functions
+ *****************************/
+
+
+ /* LookupType 1 */
+
+ /* SingleSubstFormat1 */
+ /* SingleSubstFormat2 */
+
+ FT_Error Load_SingleSubst( TTO_SingleSubst* ss,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ FT_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 = Load_Coverage( &ss->Coverage, stream ) ) != TT_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, FT_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 TTO_Err_Invalid_GSUB_SubTable_Format;
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ FREE( s );
+
+ Fail2:
+ Free_Coverage( &ss->Coverage, memory );
+ return error;
+ }
+
+
+ void Free_SingleSubst( TTO_SingleSubst* ss,
+ FT_Memory memory )
+ {
+ switch ( ss->SubstFormat )
+ {
+ case 1:
+ break;
+
+ case 2:
+ FREE( ss->ssf.ssf2.Substitute );
+ break;
+ }
+
+ Free_Coverage( &ss->Coverage, memory );
+ }
+
+
+ static FT_Error Lookup_SingleSubst( TTO_SingleSubst* ss,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ TTO_GDEFHeader* gdef )
+ {
+ FT_UShort index, value[1], property;
+ FT_Error error;
+
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return TTO_Err_Not_Covered;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &ss->Coverage, in->string[in->pos], &index );
+ if ( error )
+ return error;
+
+ switch ( ss->SubstFormat )
+ {
+ case 1:
+ value[0] = ( in->string[in->pos] + ss->ssf.ssf1.DeltaGlyphID ) & 0xFFFF;
+ if ( ADD_String( in, 1, out, 1, value, 0xFFFF, 0xFFFF ) )
+ return error;
+ break;
+
+ case 2:
+ if ( index >= ss->ssf.ssf2.GlyphCount )
+ return TTO_Err_Invalid_GSUB_SubTable;
+ value[0] = ss->ssf.ssf2.Substitute[index];
+ if ( ADD_String( in, 1, out, 1, value, 0xFFFF, 0xFFFF ) )
+ return error;
+ break;
+
+ default:
+ return TTO_Err_Invalid_GSUB_SubTable;
+ }
+
+ if ( gdef && gdef->NewGlyphClasses )
+ {
+ /* we inherit the old glyph class to the substituted glyph */
+
+ error = Add_Glyph_Property( gdef, value[0], property );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 2 */
+
+ /* Sequence */
+
+ static FT_Error Load_Sequence( TTO_Sequence* s,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_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, FT_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 TT_Err_Ok;
+ }
+
+
+ static void Free_Sequence( TTO_Sequence* s,
+ FT_Memory memory )
+ {
+ FREE( s->Substitute );
+ }
+
+
+ /* MultipleSubstFormat1 */
+
+ FT_Error Load_MultipleSubst( TTO_MultipleSubst* ms,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &ms->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_Sequence( &s[n], memory );
+
+ FREE( s );
+
+ Fail2:
+ Free_Coverage( &ms->Coverage, memory );
+ return error;
+ }
+
+
+ void Free_MultipleSubst( TTO_MultipleSubst* ms,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_Sequence* s;
+
+
+ if ( ms->Sequence )
+ {
+ count = ms->SequenceCount;
+ s = ms->Sequence;
+
+ for ( n = 0; n < count; n++ )
+ Free_Sequence( &s[n], memory );
+
+ FREE( s );
+ }
+
+ Free_Coverage( &ms->Coverage, memory );
+ }
+
+
+ static FT_Error Lookup_MultipleSubst( TTO_MultipleSubst* ms,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ TTO_GDEFHeader* gdef )
+ {
+ FT_Error error;
+ FT_UShort index, property, n, count;
+ FT_UShort*s;
+
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return TTO_Err_Not_Covered;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &ms->Coverage, in->string[in->pos], &index );
+ if ( error )
+ return error;
+
+ if ( index >= ms->SequenceCount )
+ return TTO_Err_Invalid_GSUB_SubTable;
+
+ count = ms->Sequence[index].GlyphCount;
+ s = ms->Sequence[index].Substitute;
+
+ if ( ADD_String( in, 1, out, count, s, 0xFFFF, 0xFFFF ) )
+ return error;
+
+ if ( gdef && gdef->NewGlyphClasses )
+ {
+ /* this is a guess only ... */
+
+ if ( property == TTO_LIGATURE )
+ property = TTO_BASE_GLYPH;
+
+ for ( n = 0; n < count; n++ )
+ {
+ error = Add_Glyph_Property( gdef, s[n], property );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ }
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 3 */
+
+ /* AlternateSet */
+
+ static FT_Error Load_AlternateSet( TTO_AlternateSet* as,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_UShort* a;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = as->GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ as->Alternate = NULL;
+
+ if ( ALLOC_ARRAY( as->Alternate, count, FT_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 TT_Err_Ok;
+ }
+
+
+ static void Free_AlternateSet( TTO_AlternateSet* as,
+ FT_Memory memory )
+ {
+ FREE( as->Alternate );
+ }
+
+
+ /* AlternateSubstFormat1 */
+
+ FT_Error Load_AlternateSubst( TTO_AlternateSubst* as,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &as->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_AlternateSet( &aset[n], memory );
+
+ FREE( aset );
+
+ Fail2:
+ Free_Coverage( &as->Coverage, memory );
+ return error;
+ }
+
+
+ void Free_AlternateSubst( TTO_AlternateSubst* as,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_AlternateSet* aset;
+
+
+ if ( as->AlternateSet )
+ {
+ count = as->AlternateSetCount;
+ aset = as->AlternateSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_AlternateSet( &aset[n], memory );
+
+ FREE( aset );
+ }
+
+ Free_Coverage( &as->Coverage, memory );
+ }
+
+
+ static FT_Error Lookup_AlternateSubst( TTO_GSUBHeader* gsub,
+ TTO_AlternateSubst* as,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ TTO_GDEFHeader* gdef )
+ {
+ FT_Error error;
+ FT_UShort index, alt_index, property;
+
+ TTO_AlternateSet aset;
+
+
+ if ( context_length != 0xFFFF && context_length < 1 )
+ return TTO_Err_Not_Covered;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &as->Coverage, in->string[in->pos], &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)( out->pos, in->string[in->pos],
+ aset.GlyphCount, aset.Alternate,
+ gsub->data );
+ else
+ alt_index = 0;
+
+ if ( ADD_String( in, 1, out, 1, &aset.Alternate[alt_index],
+ 0xFFFF, 0xFFFF ) )
+ return error;
+
+ if ( gdef && gdef->NewGlyphClasses )
+ {
+ /* we inherit the old glyph class to the substituted glyph */
+
+ error = Add_Glyph_Property( gdef, aset.Alternate[alt_index],
+ property );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 4 */
+
+ /* Ligature */
+
+ static FT_Error Load_Ligature( TTO_Ligature* l,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_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, FT_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 TT_Err_Ok;
+ }
+
+
+ static void Free_Ligature( TTO_Ligature* l,
+ FT_Memory memory )
+ {
+ FREE( l->Component );
+ }
+
+
+ /* LigatureSet */
+
+ static FT_Error Load_LigatureSet( TTO_LigatureSet* ls,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_Ligature( &l[n], memory );
+
+ FREE( l );
+ return error;
+ }
+
+
+ static void Free_LigatureSet( TTO_LigatureSet* ls,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_Ligature* l;
+
+
+ if ( ls->Ligature )
+ {
+ count = ls->LigatureCount;
+ l = ls->Ligature;
+
+ for ( n = 0; n < count; n++ )
+ Free_Ligature( &l[n], memory );
+
+ FREE( l );
+ }
+ }
+
+
+ /* LigatureSubstFormat1 */
+
+ FT_Error Load_LigatureSubst( TTO_LigatureSubst* ls,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &ls->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_LigatureSet( &lset[n], memory );
+
+ FREE( lset );
+
+ Fail2:
+ Free_Coverage( &ls->Coverage, memory );
+ return error;
+ }
+
+
+ void Free_LigatureSubst( TTO_LigatureSubst* ls,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_LigatureSet* lset;
+
+
+ if ( ls->LigatureSet )
+ {
+ count = ls->LigatureSetCount;
+ lset = ls->LigatureSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_LigatureSet( &lset[n], memory );
+
+ FREE( lset );
+ }
+
+ Free_Coverage( &ls->Coverage, memory );
+ }
+
+
+ static FT_Error Lookup_LigatureSubst( TTO_LigatureSubst* ls,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ TTO_GDEFHeader* gdef )
+ {
+ FT_UShort index, property;
+ FT_Error error;
+ FT_UShort numlig, i, j, is_mark, first_is_mark = FALSE;
+ FT_UShort* s_in;
+ FT_UShort* c;
+
+ TTO_Ligature* lig;
+
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ if ( property == TTO_MARK )
+ first_is_mark = TRUE;
+
+ error = Coverage_Index( &ls->Coverage, in->string[in->pos], &index );
+ if ( error )
+ return error;
+
+ if ( index >= ls->LigatureSetCount )
+ return TTO_Err_Invalid_GSUB_SubTable;
+
+ lig = ls->LigatureSet[index].Ligature;
+
+ for ( numlig = ls->LigatureSet[index].LigatureCount;
+ numlig;
+ numlig--, lig++ )
+ {
+ if ( in->pos + lig->ComponentCount > in->length )
+ continue; /* Not enough glyphs in input */
+
+ s_in = &in->string[in->pos];
+ c = lig->Component;
+
+ is_mark = first_is_mark;
+
+ if ( context_length != 0xFFFF && context_length < lig->ComponentCount )
+ break;
+
+ for ( i = 1, j = 1; i < lig->ComponentCount; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( in->pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( property != TTO_MARK )
+ is_mark = FALSE;
+
+ if ( s_in[j] != c[i - 1] )
+ break;
+ }
+
+ if ( i == lig->ComponentCount )
+ {
+ if ( gdef && gdef->NewGlyphClasses )
+ {
+ /* this is just a guess ... */
+
+ error = Add_Glyph_Property( gdef, lig->LigGlyph,
+ is_mark ? TTO_MARK : TTO_LIGATURE );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ }
+
+ if ( i == j )
+ {
+ /* We don't use a new ligature ID if there are no skipped
+ glyphs and the ligature already has an ID. */
+
+ if ( in->ligIDs[in->pos] )
+ {
+ if ( ADD_String( in, i, out, 1, &lig->LigGlyph,
+ 0xFFFF, 0xFFFF ) )
+ return error;
+ }
+ else
+ {
+ if ( ADD_String( in, i, out, 1, &lig->LigGlyph,
+ 0xFFFF, in->max_ligID ) )
+ return error;
+
+ (in->max_ligID)++;
+ }
+ }
+ else
+ {
+ if ( ADD_String( in, 1, out, 1, &lig->LigGlyph,
+ 0xFFFF, in->max_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( gdef, in->string[in->pos],
+ flags, &property ) )
+ if ( ADD_String( in, 1, out, 1, &in->string[in->pos],
+ i, in->max_ligID ) )
+ return error;
+
+ (in->pos)++;
+ }
+
+ (in->max_ligID)++;
+ }
+
+ return TT_Err_Ok;
+ }
+ }
+
+ return TTO_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 FT_Error Do_ContextSubst( TTO_GSUBHeader* gsub,
+ FT_UShort GlyphCount,
+ FT_UShort SubstCount,
+ TTO_SubstLookupRecord* subst,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ int nesting_level )
+ {
+ FT_Error error;
+ FT_UShort i, old_pos;
+
+
+ i = 0;
+
+ while ( i < GlyphCount )
+ {
+ if ( SubstCount && i == subst->SequenceIndex )
+ {
+ old_pos = in->pos;
+
+ /* Do a substitution */
+
+ error = Do_Glyph_Lookup( gsub, subst->LookupListIndex, in, out,
+ GlyphCount, nesting_level );
+
+ subst++;
+ SubstCount--;
+ i += in->pos - old_pos;
+
+ if ( error == TTO_Err_Not_Covered )
+ {
+ /* XXX "can't happen" -- but don't count on it */
+
+ if ( ADD_String( in, 1, out, 1, &in->string[in->pos],
+ 0xFFFF, 0xFFFF ) )
+ return error;
+ i++;
+ }
+ else if ( error )
+ return error;
+ }
+ else
+ {
+ /* No substitution for this index */
+
+ if ( ADD_String( in, 1, out, 1, &in->string[in->pos],
+ 0xFFFF, 0xFFFF ) )
+ return error;
+ i++;
+ }
+ }
+
+ return TT_Err_Ok;
+ }
+
+
+ /* LookupType 5 */
+
+ /* SubRule */
+
+ static FT_Error Load_SubRule( TTO_SubRule* sr,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_UShort* i;
+
+ TTO_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, FT_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, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( slr );
+
+ Fail2:
+ FREE( i );
+ return error;
+ }
+
+
+ static void Free_SubRule( TTO_SubRule* sr,
+ FT_Memory memory )
+ {
+ FREE( sr->SubstLookupRecord );
+ FREE( sr->Input );
+ }
+
+
+ /* SubRuleSet */
+
+ static FT_Error Load_SubRuleSet( TTO_SubRuleSet* srs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_SubRule( &sr[n], memory );
+
+ FREE( sr );
+ return error;
+ }
+
+
+ static void Free_SubRuleSet( TTO_SubRuleSet* srs,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_SubRule* sr;
+
+
+ if ( srs->SubRule )
+ {
+ count = srs->SubRuleCount;
+ sr = srs->SubRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubRule( &sr[n], memory );
+
+ FREE( sr );
+ }
+ }
+
+
+ /* ContextSubstFormat1 */
+
+ static FT_Error Load_ContextSubst1( TTO_ContextSubstFormat1* csf1,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &csf1->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_SubRuleSet( &srs[n], memory );
+
+ FREE( srs );
+
+ Fail2:
+ Free_Coverage( &csf1->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_Context1( TTO_ContextSubstFormat1* csf1,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_SubRuleSet* srs;
+
+
+ if ( csf1->SubRuleSet )
+ {
+ count = csf1->SubRuleSetCount;
+ srs = csf1->SubRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubRuleSet( &srs[n], memory );
+
+ FREE( srs );
+ }
+
+ Free_Coverage( &csf1->Coverage, memory );
+ }
+
+
+ /* SubClassRule */
+
+ static FT_Error Load_SubClassRule( TTO_ContextSubstFormat2* csf2,
+ TTO_SubClassRule* scr,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ FT_UShort* c;
+ TTO_SubstLookupRecord* slr;
+ FT_Bool* d;
+
+
+ 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, FT_UShort ) )
+ return error;
+
+ c = scr->Class;
+ d = csf2->ClassDef.Defined;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ {
+ c[n] = GET_UShort();
+
+ /* We check whether the specific class is used at all. If not,
+ class 0 is used instead. */
+ if ( !d[c[n]] )
+ c[n] = 0;
+ }
+
+ FORGET_Frame();
+
+ scr->SubstLookupRecord = NULL;
+
+ count = scr->SubstCount;
+
+ if ( ALLOC_ARRAY( scr->SubstLookupRecord, count, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( slr );
+
+ Fail2:
+ FREE( c );
+ return error;
+ }
+
+
+ static void Free_SubClassRule( TTO_SubClassRule* scr,
+ FT_Memory memory )
+ {
+ FREE( scr->SubstLookupRecord );
+ FREE( scr->Class );
+ }
+
+
+ /* SubClassSet */
+
+ static FT_Error Load_SubClassSet( TTO_ContextSubstFormat2* csf2,
+ TTO_SubClassSet* scs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_SubClassRule( &scr[n], memory );
+
+ FREE( scr );
+ return error;
+ }
+
+
+ static void Free_SubClassSet( TTO_SubClassSet* scs,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_SubClassRule* scr;
+
+
+ if ( scs->SubClassRule )
+ {
+ count = scs->SubClassRuleCount;
+ scr = scs->SubClassRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubClassRule( &scr[n], memory );
+
+ FREE( scr );
+ }
+ }
+
+
+ /* ContextSubstFormat2 */
+
+ static FT_Error Load_ContextSubst2( TTO_ContextSubstFormat2* csf2,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &csf2->Coverage, stream ) ) != TT_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 = Load_ClassDefinition( &csf2->ClassDef, count,
+ stream ) ) != TT_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ csf2->SubClassSet = NULL;
+ csf2->MaxContextLength = 0;
+
+ if ( ALLOC_ARRAY( csf2->SubClassSet, count, TTO_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 ) ) != TT_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 TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_SubClassSet( &scs[n], memory );
+
+ FREE( scs );
+
+ Fail2:
+ Free_ClassDefinition( &csf2->ClassDef, memory );
+
+ Fail3:
+ Free_Coverage( &csf2->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_Context2( TTO_ContextSubstFormat2* csf2,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_SubClassSet* scs;
+
+
+ if ( csf2->SubClassSet )
+ {
+ count = csf2->SubClassSetCount;
+ scs = csf2->SubClassSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubClassSet( &scs[n], memory );
+
+ FREE( scs );
+ }
+
+ Free_ClassDefinition( &csf2->ClassDef, memory );
+ Free_Coverage( &csf2->Coverage, memory );
+ }
+
+
+ /* ContextSubstFormat3 */
+
+ static FT_Error Load_ContextSubst3( TTO_ContextSubstFormat3* csf3,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_Coverage* c;
+ TTO_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, TTO_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 = Load_Coverage( &c[n], stream ) ) != TT_Err_Ok )
+ goto Fail2;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ csf3->SubstLookupRecord = NULL;
+
+ count = csf3->SubstCount;
+
+ if ( ALLOC_ARRAY( csf3->SubstLookupRecord, count,
+ TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( slr );
+
+ Fail2:
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ return error;
+ }
+
+
+ static void Free_Context3( TTO_ContextSubstFormat3* csf3,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_Coverage* c;
+
+
+ FREE( csf3->SubstLookupRecord );
+
+ if ( csf3->Coverage )
+ {
+ count = csf3->GlyphCount;
+ c = csf3->Coverage;
+
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ }
+ }
+
+
+ /* ContextSubst */
+
+ FT_Error Load_ContextSubst( TTO_ContextSubst* cs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ 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 TTO_Err_Invalid_GSUB_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+ void Free_ContextSubst( TTO_ContextSubst* cs,
+ FT_Memory memory )
+ {
+ switch ( cs->SubstFormat )
+ {
+ case 1:
+ Free_Context1( &cs->csf.csf1, memory );
+ break;
+
+ case 2:
+ Free_Context2( &cs->csf.csf2, memory );
+ break;
+
+ case 3:
+ Free_Context3( &cs->csf.csf3, memory );
+ break;
+ }
+ }
+
+
+ static FT_Error Lookup_ContextSubst1(
+ TTO_GSUBHeader* gsub,
+ TTO_ContextSubstFormat1* csf1,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, property;
+ FT_UShort i, j, k, numsr;
+ FT_Error error;
+ FT_UShort* s_in;
+
+ TTO_SubRule* sr;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gsub->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &csf1->Coverage, in->string[in->pos], &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 )
+ continue;
+
+ if ( in->pos + sr[k].GlyphCount > in->length )
+ continue; /* context is too long */
+
+ s_in = &in->string[in->pos];
+
+ for ( i = 1, j = 1; i < sr[k].GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( in->pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( s_in[j] != sr[k].Input[i - 1] )
+ break;
+ }
+
+ if ( i == sr[k].GlyphCount )
+ return Do_ContextSubst( gsub, sr[k].GlyphCount,
+ sr[k].SubstCount, sr[k].SubstLookupRecord,
+ in, out,
+ nesting_level );
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ static FT_Error Lookup_ContextSubst2(
+ TTO_GSUBHeader* gsub,
+ TTO_ContextSubstFormat2* csf2,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, property;
+ FT_Error error;
+ FT_Memory memory = gsub->memory;
+ FT_UShort i, j, k, known_classes;
+
+ FT_UShort* classes;
+ FT_UShort* s_in;
+ FT_UShort* cl;
+
+ TTO_SubClassSet* scs;
+ TTO_SubClassRule* sr;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gsub->gdef;
+
+ if ( ALLOC_ARRAY( classes, csf2->MaxContextLength, FT_UShort ) )
+ return error;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], 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 = Coverage_Index( &csf2->Coverage, in->string[in->pos], &index );
+ if ( error )
+ goto End;
+
+ error = Get_Class( &csf2->ClassDef, in->string[in->pos],
+ &classes[0], NULL );
+ if ( error )
+ goto End;
+ known_classes = 0;
+
+ scs = &csf2->SubClassSet[classes[0]];
+ if ( !scs )
+ {
+ error = TTO_Err_Invalid_GSUB_SubTable;
+ goto End;
+ }
+
+ for ( k = 0; k < scs->SubClassRuleCount; k++ )
+ {
+ sr = &scs->SubClassRule[k];
+
+ if ( context_length != 0xFFFF && context_length < sr->GlyphCount )
+ continue;
+
+ if ( in->pos + sr->GlyphCount > in->length )
+ continue; /* context is too long */
+
+ s_in = &in->string[in->pos];
+ cl = sr->Class;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = 1; i < sr->GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( in->pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( i > known_classes )
+ {
+ /* Keeps us from having to do this for each rule */
+
+ error = Get_Class( &csf2->ClassDef, s_in[j], &classes[i], NULL );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ known_classes = i;
+ }
+
+ if ( cl[i - 1] != classes[i] )
+ break;
+ }
+
+ if ( i == sr->GlyphCount )
+ {
+ error = Do_ContextSubst( gsub, sr->GlyphCount,
+ sr->SubstCount, sr->SubstLookupRecord,
+ in, out,
+ nesting_level );
+ goto End;
+ }
+ }
+
+ error = TTO_Err_Not_Covered;
+
+ End:
+ FREE( classes );
+ return error;
+ }
+
+
+ static FT_Error Lookup_ContextSubst3(
+ TTO_GSUBHeader* gsub,
+ TTO_ContextSubstFormat3* csf3,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_Error error;
+ FT_UShort index, i, j, property;
+ FT_UShort* s_in;
+
+ TTO_Coverage* c;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gsub->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ if ( context_length != 0xFFFF && context_length < csf3->GlyphCount )
+ return TTO_Err_Not_Covered;
+
+ if ( in->pos + csf3->GlyphCount > in->length )
+ return TTO_Err_Not_Covered; /* context is too long */
+
+ s_in = &in->string[in->pos];
+ c = csf3->Coverage;
+
+ for ( i = 1, j = 1; i < csf3->GlyphCount; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( in->pos + j < in->length )
+ j++;
+ else
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &c[i], s_in[j], &index );
+ if ( error )
+ return error;
+ }
+
+ return Do_ContextSubst( gsub, csf3->GlyphCount,
+ csf3->SubstCount, csf3->SubstLookupRecord,
+ in, out,
+ nesting_level );
+ }
+
+
+ static FT_Error Lookup_ContextSubst( TTO_GSUBHeader* gsub,
+ TTO_ContextSubst* cs,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ switch ( cs->SubstFormat )
+ {
+ case 1:
+ return Lookup_ContextSubst1( gsub, &cs->csf.csf1, in, out,
+ flags, context_length, nesting_level );
+
+ case 2:
+ return Lookup_ContextSubst2( gsub, &cs->csf.csf2, in, out,
+ flags, context_length, nesting_level );
+
+ case 3:
+ return Lookup_ContextSubst3( gsub, &cs->csf.csf3, in, out,
+ flags, context_length, nesting_level );
+
+ default:
+ return TTO_Err_Invalid_GSUB_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+ /* LookupType 6 */
+
+ /* ChainSubRule */
+
+ static FT_Error Load_ChainSubRule( TTO_ChainSubRule* csr,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_UShort* b;
+ FT_UShort* i;
+ FT_UShort* l;
+
+ TTO_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, FT_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, FT_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, FT_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, TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( slr );
+
+ Fail2:
+ FREE( l );
+
+ Fail3:
+ FREE( i );
+
+ Fail4:
+ FREE( b );
+ return error;
+ }
+
+
+ static void Free_ChainSubRule( TTO_ChainSubRule* csr,
+ FT_Memory memory )
+ {
+ FREE( csr->SubstLookupRecord );
+ FREE( csr->Lookahead );
+ FREE( csr->Input );
+ FREE( csr->Backtrack );
+ }
+
+
+ /* ChainSubRuleSet */
+
+ static FT_Error Load_ChainSubRuleSet( TTO_ChainSubRuleSet* csrs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubRule( &csr[n], memory );
+
+ FREE( csr );
+ return error;
+ }
+
+
+ static void Free_ChainSubRuleSet( TTO_ChainSubRuleSet* csrs,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ChainSubRule* csr;
+
+
+ if ( csrs->ChainSubRule )
+ {
+ count = csrs->ChainSubRuleCount;
+ csr = csrs->ChainSubRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubRule( &csr[n], memory );
+
+ FREE( csr );
+ }
+ }
+
+
+ /* ChainContextSubstFormat1 */
+
+ static FT_Error Load_ChainContextSubst1(
+ TTO_ChainContextSubstFormat1* ccsf1,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 = Load_Coverage( &ccsf1->Coverage, stream ) ) != TT_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, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubRuleSet( &csrs[n], memory );
+
+ FREE( csrs );
+
+ Fail2:
+ Free_Coverage( &ccsf1->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_ChainContext1( TTO_ChainContextSubstFormat1* ccsf1,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ChainSubRuleSet* csrs;
+
+
+ if ( ccsf1->ChainSubRuleSet )
+ {
+ count = ccsf1->ChainSubRuleSetCount;
+ csrs = ccsf1->ChainSubRuleSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubRuleSet( &csrs[n], memory );
+
+ FREE( csrs );
+ }
+
+ Free_Coverage( &ccsf1->Coverage, memory );
+ }
+
+
+ /* ChainSubClassRule */
+
+ static FT_Error Load_ChainSubClassRule(
+ TTO_ChainContextSubstFormat2* ccsf2,
+ TTO_ChainSubClassRule* cscr,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ FT_UShort* b;
+ FT_UShort* i;
+ FT_UShort* l;
+ TTO_SubstLookupRecord* slr;
+ FT_Bool* d;
+
+
+ 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, FT_UShort ) )
+ return error;
+
+ b = cscr->Backtrack;
+ d = ccsf2->BacktrackClassDef.Defined;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail4;
+
+ for ( n = 0; n < count; n++ )
+ {
+ b[n] = GET_UShort();
+
+ /* We check whether the specific class is used at all. If not,
+ class 0 is used instead. */
+
+ if ( !d[b[n]] )
+ b[n] = 0;
+ }
+
+ 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, FT_UShort ) )
+ goto Fail4;
+
+ i = cscr->Input;
+ d = ccsf2->InputClassDef.Defined;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail3;
+
+ for ( n = 0; n < count; n++ )
+ {
+ i[n] = GET_UShort();
+
+ if ( !d[i[n]] )
+ i[n] = 0;
+ }
+
+ 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, FT_UShort ) )
+ goto Fail3;
+
+ l = cscr->Lookahead;
+ d = ccsf2->LookaheadClassDef.Defined;
+
+ if ( ACCESS_Frame( count * 2L ) )
+ goto Fail2;
+
+ for ( n = 0; n < count; n++ )
+ {
+ l[n] = GET_UShort();
+
+ if ( !d[l[n]] )
+ l[n] = 0;
+ }
+
+ 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,
+ TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( slr );
+
+ Fail2:
+ FREE( l );
+
+ Fail3:
+ FREE( i );
+
+ Fail4:
+ FREE( b );
+ return error;
+ }
+
+
+ static void Free_ChainSubClassRule( TTO_ChainSubClassRule* cscr,
+ FT_Memory memory )
+ {
+ FREE( cscr->SubstLookupRecord );
+ FREE( cscr->Lookahead );
+ FREE( cscr->Input );
+ FREE( cscr->Backtrack );
+ }
+
+
+ /* SubClassSet */
+
+ static FT_Error Load_ChainSubClassSet(
+ TTO_ChainContextSubstFormat2* ccsf2,
+ TTO_ChainSubClassSet* cscs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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,
+ TTO_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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubClassRule( &cscr[n], memory );
+
+ FREE( cscr );
+ return error;
+ }
+
+
+ static void Free_ChainSubClassSet( TTO_ChainSubClassSet* cscs,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ChainSubClassRule* cscr;
+
+
+ if ( cscs->ChainSubClassRule )
+ {
+ count = cscs->ChainSubClassRuleCount;
+ cscr = cscs->ChainSubClassRule;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubClassRule( &cscr[n], memory );
+
+ FREE( cscr );
+ }
+ }
+
+
+ /* ChainContextSubstFormat2 */
+
+ static FT_Error Load_ChainContextSubst2(
+ TTO_ChainContextSubstFormat2* ccsf2,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+ FT_ULong backtrack_offset, input_offset, lookahead_offset;
+
+ TTO_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 = Load_Coverage( &ccsf2->Coverage, stream ) ) != TT_Err_Ok )
+ return error;
+ (void)FILE_Seek( cur_offset );
+
+ if ( ACCESS_Frame( 8L ) )
+ goto Fail5;
+
+ backtrack_offset = GET_UShort() + base_offset;
+ input_offset = GET_UShort() + base_offset;
+ lookahead_offset = GET_UShort() + base_offset;
+
+ /* `ChainSubClassSetCount' is the upper limit for input class values,
+ thus we read it now to make an additional safety check. */
+
+ count = ccsf2->ChainSubClassSetCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( backtrack_offset ) ||
+ ( error = Load_ClassDefinition( &ccsf2->BacktrackClassDef, count,
+ stream ) ) != TT_Err_Ok )
+ goto Fail5;
+ if ( FILE_Seek( input_offset ) ||
+ ( error = Load_ClassDefinition( &ccsf2->InputClassDef, count,
+ stream ) ) != TT_Err_Ok )
+ goto Fail4;
+ if ( FILE_Seek( lookahead_offset ) ||
+ ( error = Load_ClassDefinition( &ccsf2->LookaheadClassDef, count,
+ stream ) ) != TT_Err_Ok )
+ goto Fail3;
+ (void)FILE_Seek( cur_offset );
+
+ ccsf2->ChainSubClassSet = NULL;
+ ccsf2->MaxBacktrackLength = 0;
+ ccsf2->MaxInputLength = 0;
+ ccsf2->MaxLookaheadLength = 0;
+
+ if ( ALLOC_ARRAY( ccsf2->ChainSubClassSet, count, TTO_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 ) ) != TT_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 TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubClassSet( &cscs[n], memory );
+
+ FREE( cscs );
+
+ Fail2:
+ Free_ClassDefinition( &ccsf2->LookaheadClassDef, memory );
+
+ Fail3:
+ Free_ClassDefinition( &ccsf2->InputClassDef, memory );
+
+ Fail4:
+ Free_ClassDefinition( &ccsf2->BacktrackClassDef, memory );
+
+ Fail5:
+ Free_Coverage( &ccsf2->Coverage, memory );
+ return error;
+ }
+
+
+ static void Free_ChainContext2( TTO_ChainContextSubstFormat2* ccsf2,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ChainSubClassSet* cscs;
+
+
+ if ( ccsf2->ChainSubClassSet )
+ {
+ count = ccsf2->ChainSubClassSetCount;
+ cscs = ccsf2->ChainSubClassSet;
+
+ for ( n = 0; n < count; n++ )
+ Free_ChainSubClassSet( &cscs[n], memory );
+
+ FREE( cscs );
+ }
+
+ Free_ClassDefinition( &ccsf2->LookaheadClassDef, memory );
+ Free_ClassDefinition( &ccsf2->InputClassDef, memory );
+ Free_ClassDefinition( &ccsf2->BacktrackClassDef, memory );
+
+ Free_Coverage( &ccsf2->Coverage, memory );
+ }
+
+
+ /* ChainContextSubstFormat3 */
+
+ static FT_Error Load_ChainContextSubst3(
+ TTO_ChainContextSubstFormat3* ccsf3,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_UShort backtrack_count, input_count, lookahead_count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_Coverage* b;
+ TTO_Coverage* i;
+ TTO_Coverage* l;
+ TTO_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,
+ TTO_Coverage ) )
+ return error;
+
+ b = ccsf3->BacktrackCoverage;
+
+ for ( n = 0; n < backtrack_count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail4;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Coverage( &b[n], stream ) ) != TT_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, TTO_Coverage ) )
+ goto Fail4;
+
+ i = ccsf3->InputCoverage;
+
+ for ( n = 0; n < input_count; n++ )
+ {
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail3;
+
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Coverage( &i[n], stream ) ) != TT_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,
+ TTO_Coverage ) )
+ goto Fail3;
+
+ l = ccsf3->LookaheadCoverage;
+
+ for ( n = 0; n < lookahead_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 = Load_Coverage( &l[n], stream ) ) != TT_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,
+ TTO_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 TT_Err_Ok;
+
+ Fail1:
+ FREE( slr );
+
+ Fail2:
+ for ( n = 0; n < lookahead_count; n++ )
+ Free_Coverage( &l[n], memory );
+
+ FREE( l );
+
+ Fail3:
+ for ( n = 0; n < input_count; n++ )
+ Free_Coverage( &i[n], memory );
+
+ FREE( i );
+
+ Fail4:
+ for ( n = 0; n < backtrack_count; n++ )
+ Free_Coverage( &b[n], memory );
+
+ FREE( b );
+ return error;
+ }
+
+
+ static void Free_ChainContext3( TTO_ChainContextSubstFormat3* ccsf3,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_Coverage* c;
+
+
+ FREE( ccsf3->SubstLookupRecord );
+
+ if ( ccsf3->LookaheadCoverage )
+ {
+ count = ccsf3->LookaheadGlyphCount;
+ c = ccsf3->LookaheadCoverage;
+
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ }
+
+ if ( ccsf3->InputCoverage )
+ {
+ count = ccsf3->InputGlyphCount;
+ c = ccsf3->InputCoverage;
+
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ }
+
+ if ( ccsf3->BacktrackCoverage )
+ {
+ count = ccsf3->BacktrackGlyphCount;
+ c = ccsf3->BacktrackCoverage;
+
+ for ( n = 0; n < count; n++ )
+ Free_Coverage( &c[n], memory );
+
+ FREE( c );
+ }
+ }
+
+
+ /* ChainContextSubst */
+
+ FT_Error Load_ChainContextSubst( TTO_ChainContextSubst* ccs,
+ FT_Stream stream )
+ {
+ FT_Error error;
+
+
+ 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 TTO_Err_Invalid_GSUB_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+ void Free_ChainContextSubst( TTO_ChainContextSubst* ccs,
+ FT_Memory memory )
+ {
+ switch ( ccs->SubstFormat )
+ {
+ case 1:
+ Free_ChainContext1( &ccs->ccsf.ccsf1, memory );
+ break;
+
+ case 2:
+ Free_ChainContext2( &ccs->ccsf.ccsf2, memory );
+ break;
+
+ case 3:
+ Free_ChainContext3( &ccs->ccsf.ccsf3, memory );
+ break;
+ }
+ }
+
+
+ static FT_Error Lookup_ChainContextSubst1(
+ TTO_GSUBHeader* gsub,
+ TTO_ChainContextSubstFormat1* ccsf1,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, property;
+ FT_UShort i, j, k, num_csr, curr_pos;
+ FT_UShort bgc, igc, lgc;
+ FT_Error error;
+ FT_UShort* s_in;
+
+ TTO_ChainSubRule* csr;
+ TTO_ChainSubRule curr_csr;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gsub->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ error = Coverage_Index( &ccsf1->Coverage, in->string[in->pos], &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 )
+ continue;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+ continue;
+
+ 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 */
+
+ curr_pos = 0;
+ s_in = &in->string[curr_pos];
+
+ for ( i = bgc, j = in->pos - 1; i > 0; i--, j-- )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( j > curr_pos )
+ j--;
+ else
+ break;
+ }
+
+ if ( s_in[j] != curr_csr.Backtrack[i - 1] )
+ break;
+ }
+
+ if ( i != 0 )
+ continue;
+ }
+
+ curr_pos = in->pos;
+ s_in = &in->string[curr_pos];
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( s_in[j] != curr_csr.Input[i - 1] )
+ break;
+ }
+
+ if ( i != igc )
+ continue;
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ curr_pos = j;
+ s_in = &in->string[curr_pos];
+
+ for ( i = 0, j = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( s_in[j] != curr_csr.Lookahead[i] )
+ break;
+ }
+
+ if ( i == lgc )
+ return Do_ContextSubst( gsub, igc,
+ curr_csr.SubstCount,
+ curr_csr.SubstLookupRecord,
+ in, out,
+ nesting_level );
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ static FT_Error Lookup_ChainContextSubst2(
+ TTO_GSUBHeader* gsub,
+ TTO_ChainContextSubstFormat2* ccsf2,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, property;
+ FT_Memory memory;
+ FT_Error error;
+ FT_UShort i, j, k, curr_pos;
+ FT_UShort bgc, igc, lgc;
+ FT_UShort known_backtrack_classes,
+ known_input_classes,
+ known_lookahead_classes;
+
+ FT_UShort* backtrack_classes;
+ FT_UShort* input_classes;
+ FT_UShort* lookahead_classes;
+
+ FT_UShort* s_in;
+
+ FT_UShort* bc;
+ FT_UShort* ic;
+ FT_UShort* lc;
+
+ TTO_ChainSubClassSet* cscs;
+ TTO_ChainSubClassRule ccsr;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gsub->gdef;
+ memory = gsub->memory;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], 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 = Coverage_Index( &ccsf2->Coverage, in->string[in->pos], &index );
+ if ( error )
+ return error;
+
+ if ( ALLOC_ARRAY( backtrack_classes, ccsf2->MaxBacktrackLength, FT_UShort ) )
+ return error;
+ known_backtrack_classes = 0;
+
+ if ( ALLOC_ARRAY( input_classes, ccsf2->MaxInputLength, FT_UShort ) )
+ goto End3;
+ known_input_classes = 1;
+
+ if ( ALLOC_ARRAY( lookahead_classes, ccsf2->MaxLookaheadLength, FT_UShort ) )
+ goto End2;
+ known_lookahead_classes = 0;
+
+ error = Get_Class( &ccsf2->InputClassDef, in->string[in->pos],
+ &input_classes[0], NULL );
+ if ( error )
+ goto End1;
+
+ cscs = &ccsf2->ChainSubClassSet[input_classes[0]];
+ if ( !cscs )
+ {
+ error = TTO_Err_Invalid_GSUB_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 )
+ continue;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+ continue;
+
+ 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. */
+
+ curr_pos = 0;
+ s_in = &in->string[curr_pos];
+ bc = ccsr.Backtrack;
+
+ for ( i = 0, j = in->pos - 1; i < bgc; i++, j-- )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( j > curr_pos )
+ j--;
+ else
+ break;
+ }
+
+ if ( i >= known_backtrack_classes )
+ {
+ /* Keeps us from having to do this for each rule */
+
+ error = Get_Class( &ccsf2->BacktrackClassDef, s_in[j],
+ &backtrack_classes[i], NULL );
+ if ( error && error != TTO_Err_Not_Covered )
+ goto End1;
+ known_backtrack_classes = i;
+ }
+
+ if ( bc[bgc - 1 - i] != backtrack_classes[i] )
+ break;
+ }
+
+ if ( i != bgc )
+ continue;
+ }
+
+ curr_pos = in->pos;
+ s_in = &in->string[curr_pos];
+ ic = ccsr.Input;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ goto End1;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( i >= known_input_classes )
+ {
+ error = Get_Class( &ccsf2->InputClassDef, s_in[j],
+ &input_classes[i], NULL );
+ if ( error && error != TTO_Err_Not_Covered )
+ goto End1;
+ known_input_classes = i;
+ }
+
+ if ( ic[i - 1] != input_classes[i] )
+ break;
+ }
+
+ if ( i != igc )
+ continue;
+
+ /* we are starting to check for lookahead glyphs right after the
+ last context glyph */
+
+ curr_pos = j;
+ s_in = &in->string[curr_pos];
+ lc = ccsr.Lookahead;
+
+ for ( i = 0, j = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ break;
+ }
+
+ if ( i >= known_lookahead_classes )
+ {
+ error = Get_Class( &ccsf2->LookaheadClassDef, s_in[j],
+ &lookahead_classes[i], NULL );
+ if ( error && error != TTO_Err_Not_Covered )
+ goto End1;
+ known_lookahead_classes = i;
+ }
+
+ if ( lc[i] != lookahead_classes[i] )
+ break;
+ }
+
+ if ( i == lgc )
+ {
+ error = Do_ContextSubst( gsub, igc,
+ ccsr.SubstCount,
+ ccsr.SubstLookupRecord,
+ in, out,
+ nesting_level );
+ goto End1;
+ }
+ }
+
+ error = TTO_Err_Not_Covered;
+
+ End1:
+ FREE( lookahead_classes );
+
+ End2:
+ FREE( input_classes );
+
+ End3:
+ FREE( backtrack_classes );
+ return error;
+ }
+
+
+ static FT_Error Lookup_ChainContextSubst3(
+ TTO_GSUBHeader* gsub,
+ TTO_ChainContextSubstFormat3* ccsf3,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_UShort index, i, j, curr_pos, property;
+ FT_UShort bgc, igc, lgc;
+ FT_Error error;
+ FT_UShort* s_in;
+
+ TTO_Coverage* bc;
+ TTO_Coverage* ic;
+ TTO_Coverage* lc;
+ TTO_GDEFHeader* gdef;
+
+
+ gdef = gsub->gdef;
+
+ if ( CHECK_Property( gdef, in->string[in->pos], flags, &property ) )
+ return error;
+
+ bgc = ccsf3->BacktrackGlyphCount;
+ igc = ccsf3->InputGlyphCount;
+ lgc = ccsf3->LookaheadGlyphCount;
+
+ if ( context_length != 0xFFFF && context_length < igc )
+ return TTO_Err_Not_Covered;
+
+ /* check whether context is too long; it is a first guess only */
+
+ if ( bgc > in->pos || in->pos + igc + lgc > in->length )
+ return TTO_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 */
+
+ curr_pos = 0;
+ s_in = &in->string[curr_pos];
+ bc = ccsf3->BacktrackCoverage;
+
+ for ( i = bgc, j = in->pos - 1; i > 0; i--, j-- )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( j > curr_pos )
+ j--;
+ else
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &bc[i - 1], s_in[j], &index );
+ if ( error )
+ return error;
+ }
+ }
+
+ curr_pos = in->pos;
+ s_in = &in->string[curr_pos];
+ ic = ccsf3->InputCoverage;
+
+ /* Start at 1 because [0] is implied */
+
+ for ( i = 1, j = 1; i < igc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &ic[i], s_in[j], &index );
+ if ( error )
+ return error;
+ }
+
+ /* we are starting for lookahead glyphs right after the last context
+ glyph */
+
+ curr_pos = j;
+ s_in = &in->string[curr_pos];
+ lc = ccsf3->LookaheadCoverage;
+
+ for ( i = 0, j = 0; i < lgc; i++, j++ )
+ {
+ while ( CHECK_Property( gdef, s_in[j], flags, &property ) )
+ {
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ if ( curr_pos + j < in->length )
+ j++;
+ else
+ return TTO_Err_Not_Covered;
+ }
+
+ error = Coverage_Index( &lc[i], s_in[j], &index );
+ if ( error )
+ return error;
+ }
+
+ return Do_ContextSubst( gsub, igc,
+ ccsf3->SubstCount,
+ ccsf3->SubstLookupRecord,
+ in, out,
+ nesting_level );
+ }
+
+
+ static FT_Error Lookup_ChainContextSubst(
+ TTO_GSUBHeader* gsub,
+ TTO_ChainContextSubst* ccs,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort flags,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ switch ( ccs->SubstFormat )
+ {
+ case 1:
+ return Lookup_ChainContextSubst1( gsub, &ccs->ccsf.ccsf1, in, out,
+ flags, context_length,
+ nesting_level );
+
+ case 2:
+ return Lookup_ChainContextSubst2( gsub, &ccs->ccsf.ccsf2, in, out,
+ flags, context_length,
+ nesting_level );
+
+ case 3:
+ return Lookup_ChainContextSubst3( gsub, &ccs->ccsf.ccsf3, in, out,
+ flags, context_length,
+ nesting_level );
+
+ default:
+ return TTO_Err_Invalid_GSUB_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+
+ /***********
+ * GSUB API
+ ***********/
+
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Select_Script( TTO_GSUBHeader* gsub,
+ FT_ULong script_tag,
+ FT_UShort* script_index )
+ {
+ FT_UShort n;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+
+
+ if ( !gsub || !script_index )
+ return TT_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 TT_Err_Ok;
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Select_Language( TTO_GSUBHeader* gsub,
+ FT_ULong language_tag,
+ FT_UShort script_index,
+ FT_UShort* language_index,
+ FT_UShort* req_feature_index )
+ {
+ FT_UShort n;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+ TTO_Script* s;
+ TTO_LangSysRecord* lsr;
+
+
+ if ( !gsub || !language_index || !req_feature_index )
+ return TT_Err_Invalid_Argument;
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return TT_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 TT_Err_Ok;
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ /* selecting 0xFFFF for language_index asks for the values of the
+ default language (DefaultLangSys) */
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Select_Feature( TTO_GSUBHeader* gsub,
+ FT_ULong feature_tag,
+ FT_UShort script_index,
+ FT_UShort language_index,
+ FT_UShort* feature_index )
+ {
+ FT_UShort n;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+ TTO_Script* s;
+ TTO_LangSysRecord* lsr;
+ TTO_LangSys* ls;
+ FT_UShort* fi;
+
+ TTO_FeatureList* fl;
+ TTO_FeatureRecord* fr;
+
+
+ if ( !gsub || !feature_index )
+ return TT_Err_Invalid_Argument;
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ fl = &gsub->FeatureList;
+ fr = fl->FeatureRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return TT_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 TT_Err_Invalid_Argument;
+
+ ls = &lsr[language_index].LangSys;
+ }
+
+ fi = ls->FeatureIndex;
+
+ for ( n = 0; n < ls->FeatureCount; n++ )
+ {
+ if ( fi[n] >= fl->FeatureCount )
+ return TTO_Err_Invalid_GSUB_SubTable_Format;
+
+ if ( feature_tag == fr[fi[n]].FeatureTag )
+ {
+ *feature_index = fi[n];
+
+ return TT_Err_Ok;
+ }
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ /* The next three functions return a null-terminated list */
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Query_Scripts( TTO_GSUBHeader* gsub,
+ FT_ULong** script_tag_list )
+ {
+ FT_UShort n;
+ FT_Error error;
+ FT_Memory memory;
+ FT_ULong* stl;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+
+
+ if ( !gsub || !script_tag_list )
+ return TT_Err_Invalid_Argument;
+
+ memory = gsub->memory;
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( ALLOC_ARRAY( stl, sl->ScriptCount + 1, FT_ULong ) )
+ return error;
+
+ for ( n = 0; n < sl->ScriptCount; n++ )
+ stl[n] = sr[n].ScriptTag;
+ stl[n] = 0;
+
+ *script_tag_list = stl;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Query_Languages( TTO_GSUBHeader* gsub,
+ FT_UShort script_index,
+ FT_ULong** language_tag_list )
+ {
+ FT_UShort n;
+ FT_Error error;
+ FT_Memory memory;
+ FT_ULong* ltl;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+ TTO_Script* s;
+ TTO_LangSysRecord* lsr;
+
+
+ if ( !gsub || !language_tag_list )
+ return TT_Err_Invalid_Argument;
+
+ memory = gsub->memory;
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return TT_Err_Invalid_Argument;
+
+ s = &sr[script_index].Script;
+ lsr = s->LangSysRecord;
+
+ if ( ALLOC_ARRAY( ltl, s->LangSysCount + 1, FT_ULong ) )
+ return error;
+
+ for ( n = 0; n < s->LangSysCount; n++ )
+ ltl[n] = lsr[n].LangSysTag;
+ ltl[n] = 0;
+
+ *language_tag_list = ltl;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* selecting 0xFFFF for language_index asks for the values of the
+ default language (DefaultLangSys) */
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Query_Features( TTO_GSUBHeader* gsub,
+ FT_UShort script_index,
+ FT_UShort language_index,
+ FT_ULong** feature_tag_list )
+ {
+ FT_UShort n;
+ FT_Error error;
+ FT_Memory memory;
+ FT_ULong* ftl;
+
+ TTO_ScriptList* sl;
+ TTO_ScriptRecord* sr;
+ TTO_Script* s;
+ TTO_LangSysRecord* lsr;
+ TTO_LangSys* ls;
+ FT_UShort* fi;
+
+ TTO_FeatureList* fl;
+ TTO_FeatureRecord* fr;
+
+
+ if ( !gsub || !feature_tag_list )
+ return TT_Err_Invalid_Argument;
+
+ memory = gsub->memory;
+
+ sl = &gsub->ScriptList;
+ sr = sl->ScriptRecord;
+
+ fl = &gsub->FeatureList;
+ fr = fl->FeatureRecord;
+
+ if ( script_index >= sl->ScriptCount )
+ return TT_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 TT_Err_Invalid_Argument;
+
+ ls = &lsr[language_index].LangSys;
+ }
+
+ fi = ls->FeatureIndex;
+
+ if ( ALLOC_ARRAY( ftl, ls->FeatureCount + 1, FT_ULong ) )
+ return error;
+
+ for ( n = 0; n < ls->FeatureCount; n++ )
+ {
+ if ( fi[n] >= fl->FeatureCount )
+ {
+ FREE( ftl );
+ return TTO_Err_Invalid_GSUB_SubTable_Format;
+ }
+ ftl[n] = fr[fi[n]].FeatureTag;
+ }
+ ftl[n] = 0;
+
+ *feature_tag_list = ftl;
+
+ return TT_Err_Ok;
+ }
+
+
+ /* Do an individual subtable lookup. Returns TT_Err_Ok if substitution
+ has been done, or TTO_Err_Not_Covered if not. */
+
+ static FT_Error Do_Glyph_Lookup( TTO_GSUBHeader* gsub,
+ FT_UShort lookup_index,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out,
+ FT_UShort context_length,
+ int nesting_level )
+ {
+ FT_Error error = TT_Err_Ok;
+ FT_UShort i, flags;
+ TTO_Lookup* lo;
+
+
+ nesting_level++;
+
+ if ( nesting_level > TTO_MAX_NESTING_LEVEL )
+ return TTO_Err_Too_Many_Nested_Contexts;
+
+ lo = &gsub->LookupList.Lookup[lookup_index];
+ flags = lo->LookupFlag;
+
+ for ( i = 0; i < lo->SubTableCount; i++ )
+ {
+ switch ( lo->LookupType )
+ {
+ case GSUB_LOOKUP_SINGLE:
+ error = Lookup_SingleSubst( &lo->SubTable[i].st.gsub.single,
+ in, out,
+ flags, context_length, gsub->gdef );
+ break;
+
+ case GSUB_LOOKUP_MULTIPLE:
+ error = Lookup_MultipleSubst( &lo->SubTable[i].st.gsub.multiple,
+ in, out,
+ flags, context_length, gsub->gdef );
+ break;
+
+ case GSUB_LOOKUP_ALTERNATE:
+ error = Lookup_AlternateSubst( gsub,
+ &lo->SubTable[i].st.gsub.alternate,
+ in, out,
+ flags, context_length, gsub->gdef );
+ break;
+
+ case GSUB_LOOKUP_LIGATURE:
+ error = Lookup_LigatureSubst( &lo->SubTable[i].st.gsub.ligature,
+ in, out,
+ flags, context_length, gsub->gdef );
+ break;
+
+ case GSUB_LOOKUP_CONTEXT:
+ error = Lookup_ContextSubst( gsub, &lo->SubTable[i].st.gsub.context,
+ in, out,
+ flags, context_length, nesting_level );
+ break;
+
+ case GSUB_LOOKUP_CHAIN:
+ error = Lookup_ChainContextSubst( gsub,
+ &lo->SubTable[i].st.gsub.chain,
+ in, out,
+ flags, context_length,
+ nesting_level );
+ break;
+ }
+
+ /* Check whether we have a successful substitution or an error other
+ than TTO_Err_Not_Covered */
+
+ if ( error != TTO_Err_Not_Covered )
+ return error;
+ }
+
+ return TTO_Err_Not_Covered;
+ }
+
+
+ /* apply one lookup to the input string object */
+
+ static FT_Error Do_String_Lookup( TTO_GSUBHeader* gsub,
+ FT_UShort lookup_index,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out )
+ {
+ FT_Error error = TTO_Err_Not_Covered;
+
+ FT_UShort* properties = gsub->LookupList.Properties;
+ FT_UShort* p_in = in->properties;
+ FT_UShort* s_in = in->string;
+
+ int nesting_level = 0;
+
+
+ while ( in->pos < in->length )
+ {
+ if ( ~p_in[in->pos] & properties[lookup_index] )
+ {
+ /* 0xFFFF indicates that we don't have a context length yet */
+ error = Do_Glyph_Lookup( gsub, lookup_index, in, out,
+ 0xFFFF, nesting_level );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+ }
+ else
+ error = TTO_Err_Not_Covered;
+
+ if ( error == TTO_Err_Not_Covered )
+ if ( ADD_String( in, 1, out, 1, &s_in[in->pos], 0xFFFF, 0xFFFF ) )
+ return error;
+ }
+
+ return error;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Add_Feature( TTO_GSUBHeader* gsub,
+ FT_UShort feature_index,
+ FT_UShort property )
+ {
+ FT_UShort i;
+
+ TTO_Feature feature;
+ FT_UShort* properties;
+ FT_UShort* index;
+
+
+ if ( !gsub ||
+ feature_index >= gsub->FeatureList.FeatureCount )
+ return TT_Err_Invalid_Argument;
+
+ properties = gsub->LookupList.Properties;
+
+ feature = gsub->FeatureList.FeatureRecord[feature_index].Feature;
+ index = feature.LookupListIndex;
+
+ for ( i = 0; i < feature.LookupListCount; i++ )
+ properties[index[i]] |= property;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Clear_Features( TTO_GSUBHeader* gsub )
+ {
+ FT_UShort i;
+
+ FT_UShort* properties;
+
+
+ if ( !gsub )
+ return TT_Err_Invalid_Argument;
+
+ properties = gsub->LookupList.Properties;
+
+ for ( i = 0; i < gsub->LookupList.LookupCount; i++ )
+ properties[i] = 0;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Register_Alternate_Function( TTO_GSUBHeader* gsub,
+ TTO_AltFunction altfunc,
+ void* data )
+ {
+ if ( !gsub )
+ return TT_Err_Invalid_Argument;
+
+ gsub->altfunc = altfunc;
+ gsub->data = data;
+
+ return TT_Err_Ok;
+ }
+
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_String_New( FT_Memory memory,
+ TTO_GSUB_String **result )
+ {
+ FT_Error error;
+
+ TTO_GSUB_String *str;
+
+ if ( ALLOC( str, sizeof( *str ) ) )
+ return error;
+
+ str->memory = memory;
+
+ str->length = 0;
+ str->allocated = 0;
+ str->pos = 0;
+ str->string = NULL;
+ str->properties = NULL;
+ str->components = NULL;
+ str->max_ligID = 0;
+ str->ligIDs = 0;
+ str->logClusters = 0;
+
+ *result = str;
+
+ return TT_Err_Ok;
+ }
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_String_Set_Length( TTO_GSUB_String *str,
+ FT_ULong new_length)
+ {
+ FT_Memory memory = str->memory;
+ FT_Error error;
+
+ if ( new_length > str->allocated )
+ {
+ if ( REALLOC_ARRAY( str->string, str->allocated, new_length, FT_UShort ) )
+ return error;
+ if ( REALLOC_ARRAY( str->properties, str->allocated, new_length, FT_UShort ) )
+ return error;
+ if ( REALLOC_ARRAY( str->components, str->allocated, new_length, FT_UShort ) )
+ return error;
+ if ( REALLOC_ARRAY( str->ligIDs, str->allocated, new_length, FT_UShort ) )
+ return error;
+ if ( REALLOC_ARRAY( str->logClusters, str->allocated, new_length, FT_Int ) )
+ return error;
+
+ str->allocated = new_length;
+ str->length = new_length;
+ }
+
+ return TT_Err_Ok;
+ }
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_String_Done( TTO_GSUB_String *str )
+ {
+ FT_Memory memory = str->memory;
+
+ FREE( str->string );
+ FREE( str->properties );
+ FREE( str->components );
+ FREE( str->ligIDs );
+ FREE( str->logClusters );
+
+ FREE( str );
+
+ return TT_Err_Ok;
+ }
+
+ EXPORT_FUNC
+ FT_Error TT_GSUB_Apply_String( TTO_GSUBHeader* gsub,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out )
+ {
+ FT_Error error = TTO_Err_Not_Covered;
+ FT_Memory memory = in->memory;
+ FT_UShort j;
+
+ TTO_GSUB_String tmp1;
+ TTO_GSUB_String* ptmp1;
+ TTO_GSUB_String tmp2;
+ TTO_GSUB_String* ptmp2;
+ TTO_GSUB_String* t;
+
+ FT_UShort* properties;
+
+
+ if ( !gsub ||
+ !in || !out || in->length == 0 || in->pos >= in->length )
+ return TT_Err_Invalid_Argument;
+
+ properties = gsub->LookupList.Properties;
+
+ tmp1.memory = memory;
+ tmp1.length = in->length;
+ tmp1.allocated = in->length;
+ tmp1.pos = in->pos;
+ tmp1.max_ligID = 1;
+
+ if ( ALLOC_ARRAY( tmp1.string, tmp1.length, FT_UShort ) )
+ return error;
+ MEM_Copy( tmp1.string, in->string, in->length * sizeof ( FT_UShort ) );
+
+ /* make sure that we always have a `properties', `components', and
+ `ligIDs' array in the string object */
+
+ if ( ALLOC_ARRAY( tmp1.components, tmp1.length, FT_UShort ) )
+ return error;
+ if ( ALLOC_ARRAY( tmp1.ligIDs, tmp1.length, FT_UShort ) )
+ return error;
+ if ( ALLOC_ARRAY( tmp1.properties, tmp1.length, FT_UShort ) )
+ return error;
+ if ( in->properties )
+ MEM_Copy( tmp1.properties, in->properties,
+ in->length * sizeof( FT_UShort ) );
+ if ( ALLOC_ARRAY( tmp1.logClusters, tmp1.length, FT_Int ) )
+ return error;
+ MEM_Copy( tmp1.logClusters, in->logClusters,
+ in->length * sizeof( FT_Int ) );
+
+ tmp2.memory = memory;
+ tmp2.allocated = 0;
+ tmp2.pos = 0;
+ tmp2.string = NULL;
+ tmp2.properties = NULL;
+ tmp2.components = NULL;
+ tmp2.ligIDs = NULL;
+ tmp2.logClusters = NULL;
+
+ ptmp1 = &tmp1;
+ ptmp2 = &tmp2;
+
+ for ( j = 0; j < gsub->LookupList.LookupCount; j++ )
+ if ( properties[j] )
+ {
+ error = Do_String_Lookup( gsub, j, ptmp1, ptmp2 );
+ if ( error && error != TTO_Err_Not_Covered )
+ return error;
+
+ /* flipping `in' and `out', preparing the next loop */
+
+ ptmp1->pos = in->pos;
+ ptmp2->length = ptmp2->pos;
+ ptmp2->pos = in->pos;
+ ptmp2->max_ligID = ptmp1->max_ligID;
+
+ t = ptmp2;
+ ptmp2 = ptmp1;
+ ptmp1 = t;
+ }
+
+ out->length = ptmp1->length;
+ out->pos = 0;
+ out->allocated = ptmp1->allocated;
+ out->string = ptmp1->string;
+ out->components = ptmp1->components;
+ out->ligIDs = ptmp1->ligIDs;
+ out->logClusters = ptmp1->logClusters;
+
+ if ( in->properties )
+ out->properties = ptmp1->properties;
+ else
+ {
+ FREE( ptmp1->properties );
+ out->properties = NULL;
+ }
+
+ FREE( ptmp2->string );
+ FREE( ptmp2->properties );
+ FREE( ptmp2->components );
+ FREE( ptmp2->ligIDs );
+ FREE( ptmp2->logClusters );
+
+ return error;
+ }
+
+
+/* END */
diff --git a/src/ftxgsub.h b/src/ftxgsub.h
new file mode 100644
index 00000000..3f4f8ada
--- /dev/null
+++ b/src/ftxgsub.h
@@ -0,0 +1,612 @@
+/*******************************************************************
+ *
+ * ftxgsub.h
+ *
+ * TrueType Open GSUB table support
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ ******************************************************************/
+
+#ifndef FTXOPEN_H
+#error "Don't include this file! Use ftxopen.h instead."
+#endif
+
+#ifndef FTXGSUB_H
+#define FTXGSUB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TTO_Err_Invalid_GSUB_SubTable_Format 0x1010
+#define TTO_Err_Invalid_GSUB_SubTable 0x1011
+
+
+/* Lookup types for glyph substitution */
+
+#define GSUB_LOOKUP_SINGLE 1
+#define GSUB_LOOKUP_MULTIPLE 2
+#define GSUB_LOOKUP_ALTERNATE 3
+#define GSUB_LOOKUP_LIGATURE 4
+#define GSUB_LOOKUP_CONTEXT 5
+#define GSUB_LOOKUP_CHAIN 6
+
+
+/* Use this if a feature applies to all glyphs */
+
+#define ALL_GLYPHS 0xFFFF
+
+
+ /* 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
+ TT_GSUB_Register_Alternate_Function(). The function must return an
+ index into the `alternates' array. */
+
+ typedef FT_UShort (*TTO_AltFunction)(FT_ULong pos,
+ FT_UShort glyphID,
+ FT_UShort num_alternates,
+ FT_UShort* alternates,
+ void* data );
+
+
+ struct TTO_GSUBHeader_
+ {
+ FT_Memory memory;
+
+ FT_ULong offset;
+
+ FT_Fixed Version;
+
+ TTO_ScriptList ScriptList;
+ TTO_FeatureList FeatureList;
+ TTO_LookupList LookupList;
+
+ TTO_GDEFHeader* gdef;
+
+ /* the next two fields are used for an alternate substitution callback
+ function to select the proper alternate glyph. */
+
+ TTO_AltFunction altfunc;
+ void* data;
+ };
+
+ typedef struct TTO_GSUBHeader_ TTO_GSUBHeader;
+ typedef struct TTO_GSUBHeader_* TTO_GSUB;
+
+
+ /* LookupType 1 */
+
+ struct TTO_SingleSubstFormat1_
+ {
+ FT_Short DeltaGlyphID; /* constant added to get
+ substitution glyph index */
+ };
+
+ typedef struct TTO_SingleSubstFormat1_ TTO_SingleSubstFormat1;
+
+
+ struct TTO_SingleSubstFormat2_
+ {
+ FT_UShort GlyphCount; /* number of glyph IDs in
+ Substitute array */
+ FT_UShort* Substitute; /* array of substitute glyph IDs */
+ };
+
+ typedef struct TTO_SingleSubstFormat2_ TTO_SingleSubstFormat2;
+
+
+ struct TTO_SingleSubst_
+ {
+ FT_UShort SubstFormat; /* 1 or 2 */
+ TTO_Coverage Coverage; /* Coverage table */
+
+ union
+ {
+ TTO_SingleSubstFormat1 ssf1;
+ TTO_SingleSubstFormat2 ssf2;
+ } ssf;
+ };
+
+ typedef struct TTO_SingleSubst_ TTO_SingleSubst;
+
+
+ /* LookupType 2 */
+
+ struct TTO_Sequence_
+ {
+ FT_UShort GlyphCount; /* number of glyph IDs in the
+ Substitute array */
+ FT_UShort* Substitute; /* string of glyph IDs to
+ substitute */
+ };
+
+ typedef struct TTO_Sequence_ TTO_Sequence;
+
+
+ struct TTO_MultipleSubst_
+ {
+ FT_UShort SubstFormat; /* always 1 */
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort SequenceCount; /* number of Sequence tables */
+ TTO_Sequence* Sequence; /* array of Sequence tables */
+ };
+
+ typedef struct TTO_MultipleSubst_ TTO_MultipleSubst;
+
+
+ /* LookupType 3 */
+
+ struct TTO_AlternateSet_
+ {
+ FT_UShort GlyphCount; /* number of glyph IDs in the
+ Alternate array */
+ FT_UShort* Alternate; /* array of alternate glyph IDs */
+ };
+
+ typedef struct TTO_AlternateSet_ TTO_AlternateSet;
+
+
+ struct TTO_AlternateSubst_
+ {
+ FT_UShort SubstFormat; /* always 1 */
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort AlternateSetCount;
+ /* number of AlternateSet tables */
+ TTO_AlternateSet* AlternateSet; /* array of AlternateSet tables */
+ };
+
+ typedef struct TTO_AlternateSubst_ TTO_AlternateSubst;
+
+
+ /* LookupType 4 */
+
+ struct TTO_Ligature_
+ {
+ FT_UShort LigGlyph; /* glyphID of ligature
+ to substitute */
+ FT_UShort ComponentCount; /* number of components in ligature */
+ FT_UShort* Component; /* array of component glyph IDs */
+ };
+
+ typedef struct TTO_Ligature_ TTO_Ligature;
+
+
+ struct TTO_LigatureSet_
+ {
+ FT_UShort LigatureCount; /* number of Ligature tables */
+ TTO_Ligature* Ligature; /* array of Ligature tables */
+ };
+
+ typedef struct TTO_LigatureSet_ TTO_LigatureSet;
+
+
+ struct TTO_LigatureSubst_
+ {
+ FT_UShort SubstFormat; /* always 1 */
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort LigatureSetCount; /* number of LigatureSet tables */
+ TTO_LigatureSet* LigatureSet; /* array of LigatureSet tables */
+ };
+
+ typedef struct TTO_LigatureSubst_ TTO_LigatureSubst;
+
+
+ /* needed by both lookup type 5 and 6 */
+
+ struct TTO_SubstLookupRecord_
+ {
+ FT_UShort SequenceIndex; /* index into current
+ glyph sequence */
+ FT_UShort LookupListIndex; /* Lookup to apply to that pos. */
+ };
+
+ typedef struct TTO_SubstLookupRecord_ TTO_SubstLookupRecord;
+
+
+ /* LookupType 5 */
+
+ struct TTO_SubRule_
+ {
+ FT_UShort GlyphCount; /* total number of input glyphs */
+ FT_UShort SubstCount; /* number of SubstLookupRecord
+ tables */
+ FT_UShort* Input; /* array of input glyph IDs */
+ TTO_SubstLookupRecord* SubstLookupRecord;
+ /* array of SubstLookupRecord
+ tables */
+ };
+
+ typedef struct TTO_SubRule_ TTO_SubRule;
+
+
+ struct TTO_SubRuleSet_
+ {
+ FT_UShort SubRuleCount; /* number of SubRule tables */
+ TTO_SubRule* SubRule; /* array of SubRule tables */
+ };
+
+ typedef struct TTO_SubRuleSet_ TTO_SubRuleSet;
+
+
+ struct TTO_ContextSubstFormat1_
+ {
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort SubRuleSetCount; /* number of SubRuleSet tables */
+ TTO_SubRuleSet* SubRuleSet; /* array of SubRuleSet tables */
+ };
+
+ typedef struct TTO_ContextSubstFormat1_ TTO_ContextSubstFormat1;
+
+
+ struct TTO_SubClassRule_
+ {
+ FT_UShort GlyphCount; /* total number of context classes */
+ FT_UShort SubstCount; /* number of SubstLookupRecord
+ tables */
+ FT_UShort* Class; /* array of classes */
+ TTO_SubstLookupRecord* SubstLookupRecord;
+ /* array of SubstLookupRecord
+ tables */
+ };
+
+ typedef struct TTO_SubClassRule_ TTO_SubClassRule;
+
+
+ struct TTO_SubClassSet_
+ {
+ FT_UShort SubClassRuleCount;
+ /* number of SubClassRule tables */
+ TTO_SubClassRule* SubClassRule; /* array of SubClassRule tables */
+ };
+
+ typedef struct TTO_SubClassSet_ TTO_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 TTO_ContextSubstFormat2_
+ {
+ FT_UShort MaxContextLength;
+ /* maximal context length */
+ TTO_Coverage Coverage; /* Coverage table */
+ TTO_ClassDefinition ClassDef; /* ClassDef table */
+ FT_UShort SubClassSetCount;
+ /* number of SubClassSet tables */
+ TTO_SubClassSet* SubClassSet; /* array of SubClassSet tables */
+ };
+
+ typedef struct TTO_ContextSubstFormat2_ TTO_ContextSubstFormat2;
+
+
+ struct TTO_ContextSubstFormat3_
+ {
+ FT_UShort GlyphCount; /* number of input glyphs */
+ FT_UShort SubstCount; /* number of SubstLookupRecords */
+ TTO_Coverage* Coverage; /* array of Coverage tables */
+ TTO_SubstLookupRecord* SubstLookupRecord;
+ /* array of substitution lookups */
+ };
+
+ typedef struct TTO_ContextSubstFormat3_ TTO_ContextSubstFormat3;
+
+
+ struct TTO_ContextSubst_
+ {
+ FT_UShort SubstFormat; /* 1, 2, or 3 */
+
+ union
+ {
+ TTO_ContextSubstFormat1 csf1;
+ TTO_ContextSubstFormat2 csf2;
+ TTO_ContextSubstFormat3 csf3;
+ } csf;
+ };
+
+ typedef struct TTO_ContextSubst_ TTO_ContextSubst;
+
+
+ /* LookupType 6 */
+
+ struct TTO_ChainSubRule_
+ {
+ FT_UShort BacktrackGlyphCount;
+ /* total number of backtrack glyphs */
+ FT_UShort* Backtrack; /* array of backtrack glyph IDs */
+ FT_UShort InputGlyphCount;
+ /* total number of input glyphs */
+ FT_UShort* Input; /* array of input glyph IDs */
+ FT_UShort LookaheadGlyphCount;
+ /* total number of lookahead glyphs */
+ FT_UShort* Lookahead; /* array of lookahead glyph IDs */
+ FT_UShort SubstCount; /* number of SubstLookupRecords */
+ TTO_SubstLookupRecord* SubstLookupRecord;
+ /* array of SubstLookupRecords */
+ };
+
+ typedef struct TTO_ChainSubRule_ TTO_ChainSubRule;
+
+
+ struct TTO_ChainSubRuleSet_
+ {
+ FT_UShort ChainSubRuleCount;
+ /* number of ChainSubRule tables */
+ TTO_ChainSubRule* ChainSubRule; /* array of ChainSubRule tables */
+ };
+
+ typedef struct TTO_ChainSubRuleSet_ TTO_ChainSubRuleSet;
+
+
+ struct TTO_ChainContextSubstFormat1_
+ {
+ TTO_Coverage Coverage; /* Coverage table */
+ FT_UShort ChainSubRuleSetCount;
+ /* number of ChainSubRuleSet tables */
+ TTO_ChainSubRuleSet* ChainSubRuleSet;
+ /* array of ChainSubRuleSet tables */
+ };
+
+ typedef struct TTO_ChainContextSubstFormat1_ TTO_ChainContextSubstFormat1;
+
+
+ struct TTO_ChainSubClassRule_
+ {
+ FT_UShort BacktrackGlyphCount;
+ /* total number of backtrack
+ classes */
+ FT_UShort* Backtrack; /* array of backtrack classes */
+ FT_UShort InputGlyphCount;
+ /* total number of context classes */
+ FT_UShort* Input; /* array of context classes */
+ FT_UShort LookaheadGlyphCount;
+ /* total number of lookahead
+ classes */
+ FT_UShort* Lookahead; /* array of lookahead classes */
+ FT_UShort SubstCount; /* number of SubstLookupRecords */
+ TTO_SubstLookupRecord* SubstLookupRecord;
+ /* array of substitution lookups */
+ };
+
+ typedef struct TTO_ChainSubClassRule_ TTO_ChainSubClassRule;
+
+
+ struct TTO_ChainSubClassSet_
+ {
+ FT_UShort ChainSubClassRuleCount;
+ /* number of ChainSubClassRule
+ tables */
+ TTO_ChainSubClassRule* ChainSubClassRule;
+ /* array of ChainSubClassRule
+ tables */
+ };
+
+ typedef struct TTO_ChainSubClassSet_ TTO_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 TTO_ChainContextSubstFormat2_
+ {
+ TTO_Coverage Coverage; /* Coverage table */
+
+ FT_UShort MaxBacktrackLength;
+ /* maximal backtrack length */
+ TTO_ClassDefinition BacktrackClassDef;
+ /* BacktrackClassDef table */
+ FT_UShort MaxInputLength;
+ /* maximal input length */
+ TTO_ClassDefinition InputClassDef;
+ /* InputClassDef table */
+ FT_UShort MaxLookaheadLength;
+ /* maximal lookahead length */
+ TTO_ClassDefinition LookaheadClassDef;
+ /* LookaheadClassDef table */
+
+ FT_UShort ChainSubClassSetCount;
+ /* number of ChainSubClassSet
+ tables */
+ TTO_ChainSubClassSet* ChainSubClassSet;
+ /* array of ChainSubClassSet
+ tables */
+ };
+
+ typedef struct TTO_ChainContextSubstFormat2_ TTO_ChainContextSubstFormat2;
+
+
+ struct TTO_ChainContextSubstFormat3_
+ {
+ FT_UShort BacktrackGlyphCount;
+ /* number of backtrack glyphs */
+ TTO_Coverage* BacktrackCoverage;
+ /* array of backtrack Coverage
+ tables */
+ FT_UShort InputGlyphCount;
+ /* number of input glyphs */
+ TTO_Coverage* InputCoverage;
+ /* array of input coverage
+ tables */
+ FT_UShort LookaheadGlyphCount;
+ /* number of lookahead glyphs */
+ TTO_Coverage* LookaheadCoverage;
+ /* array of lookahead coverage
+ tables */
+ FT_UShort SubstCount; /* number of SubstLookupRecords */
+ TTO_SubstLookupRecord* SubstLookupRecord;
+ /* array of substitution lookups */
+ };
+
+ typedef struct TTO_ChainContextSubstFormat3_ TTO_ChainContextSubstFormat3;
+
+
+ struct TTO_ChainContextSubst_
+ {
+ FT_UShort SubstFormat; /* 1, 2, or 3 */
+
+ union
+ {
+ TTO_ChainContextSubstFormat1 ccsf1;
+ TTO_ChainContextSubstFormat2 ccsf2;
+ TTO_ChainContextSubstFormat3 ccsf3;
+ } ccsf;
+ };
+
+ typedef struct TTO_ChainContextSubst_ TTO_ChainContextSubst;
+
+
+ union TTO_GSUB_SubTable_
+ {
+ TTO_SingleSubst single;
+ TTO_MultipleSubst multiple;
+ TTO_AlternateSubst alternate;
+ TTO_LigatureSubst ligature;
+ TTO_ContextSubst context;
+ TTO_ChainContextSubst chain;
+ };
+
+ typedef union TTO_GSUB_SubTable_ TTO_GSUB_SubTable;
+
+
+ /* A simple string object. It can both `send' and `receive' data.
+ In case of sending, `length' and `pos' will be used. In case of
+ receiving, `pos' points to the first free slot, and `allocated'
+ specifies the amount of allocated memory (and the `length' field
+ will be ignored). The routine TT_Add_String() will increase the
+ amount of memory if necessary. After end of receive, `length'
+ should be set to the value of `pos', and `pos' will be set to zero.
+
+ `properties' (which is treated as a bit field) gives the glyph's
+ properties: If a certain bit is set for a glyph, the feature which
+ has the same bit set in its property value is applied.
+
+ `components' is an internal array which tracks components of
+ ligatures. We need this for MarkToLigature Attachment Positioning
+ Subtables (in GPOS) together with `ligIDs' (which is used to mark
+ ligatures and the skipped glyphs during a ligature lookup).
+ `max_ligID' is increased after a successful ligature lookup.
+
+ NEVER modify any elements of the structure! You should rather copy
+ its contents if necessary.
+
+ TT_Add_String() will also handle allocation; you should use
+ free() in case you want to destroy the arrays in the object. */
+
+ struct TTO_GSUB_String_
+ {
+ FT_Memory memory;
+
+ FT_ULong length;
+ FT_ULong pos;
+ FT_ULong allocated;
+ FT_UShort* string;
+ FT_UShort* properties;
+ FT_UShort* components;
+ FT_UShort max_ligID;
+ FT_UShort* ligIDs;
+ FT_Int* logClusters;
+ };
+
+ typedef struct TTO_GSUB_String_ TTO_GSUB_String;
+
+
+ /* finally, the GSUB API */
+
+ /* EXPORT_DEF
+ TT_Error TT_Init_GSUB_Extension( TT_Engine engine ); */
+
+ EXPORT_DEF
+ FT_Error TT_Load_GSUB_Table( FT_Face face,
+ TTO_GSUBHeader** gsub,
+ TTO_GDEFHeader* gdef );
+
+ EXPORT_DEF
+ FT_Error TT_Done_GSUB_Table( TTO_GSUBHeader* gsub );
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_Select_Script( TTO_GSUBHeader* gsub,
+ FT_ULong script_tag,
+ FT_UShort* script_index );
+ EXPORT_DEF
+ FT_Error TT_GSUB_Select_Language( TTO_GSUBHeader* gsub,
+ FT_ULong language_tag,
+ FT_UShort script_index,
+ FT_UShort* language_index,
+ FT_UShort* req_feature_index );
+ EXPORT_DEF
+ FT_Error TT_GSUB_Select_Feature( TTO_GSUBHeader* gsub,
+ FT_ULong feature_tag,
+ FT_UShort script_index,
+ FT_UShort language_index,
+ FT_UShort* feature_index );
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_Query_Scripts( TTO_GSUBHeader* gsub,
+ FT_ULong** script_tag_list );
+ EXPORT_DEF
+ FT_Error TT_GSUB_Query_Languages( TTO_GSUBHeader* gsub,
+ FT_UShort script_index,
+ FT_ULong** language_tag_list );
+ EXPORT_DEF
+ FT_Error TT_GSUB_Query_Features( TTO_GSUBHeader* gsub,
+ FT_UShort script_index,
+ FT_UShort language_index,
+ FT_ULong** feature_tag_list );
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_Add_Feature( TTO_GSUBHeader* gsub,
+ FT_UShort feature_index,
+ FT_UShort property );
+ EXPORT_DEF
+ FT_Error TT_GSUB_Clear_Features( TTO_GSUBHeader* gsub );
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_Register_Alternate_Function( TTO_GSUBHeader* gsub,
+ TTO_AltFunction altfunc,
+ void* data );
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_String_New( FT_Memory memory,
+ TTO_GSUB_String **result );
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_String_Set_Length( TTO_GSUB_String *str,
+ FT_ULong new_length);
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_String_Done( TTO_GSUB_String *str );
+
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_Apply_String( TTO_GSUBHeader* gsub,
+ TTO_GSUB_String* in,
+ TTO_GSUB_String* out );
+
+ EXPORT_DEF
+ FT_Error TT_GSUB_Add_String( TTO_GSUB_String* in,
+ FT_UShort num_in,
+ TTO_GSUB_String* out,
+ FT_UShort num_out,
+ FT_UShort* glyph_data,
+ FT_UShort component,
+ FT_UShort ligID );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FTXGSUB_H */
+
+
+/* END */
diff --git a/src/ftxopen.c b/src/ftxopen.c
new file mode 100644
index 00000000..5ffe030a
--- /dev/null
+++ b/src/ftxopen.c
@@ -0,0 +1,1467 @@
+/*******************************************************************
+ *
+ * ftxopen.c
+ *
+ * TrueType Open common table support.
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ ******************************************************************/
+
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/ftmemory.h>
+#include <freetype/internal/tterrors.h>
+#include <freetype/internal/tttypes.h>
+
+#include "ftxopen.h"
+#include "ftxopenf.h"
+
+
+ /***************************
+ * Script related functions
+ ***************************/
+
+
+ /* LangSys */
+
+ static FT_Error Load_LangSys( TTO_LangSys* ls,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_UShort n, count;
+ FT_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, FT_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 TT_Err_Ok;
+ }
+
+
+ static void Free_LangSys( TTO_LangSys* ls,
+ FT_Memory memory )
+ {
+ FREE( ls->FeatureIndex );
+ }
+
+
+ /* Script */
+
+ static FT_Error Load_Script( TTO_Script* s,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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 ) ) != TT_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();
+
+ /* safety check; otherwise the official handling of TrueType Open
+ fonts won't work */
+
+ if ( s->LangSysCount == 0 && s->DefaultLangSys.FeatureCount == 0 )
+ {
+ error = TTO_Err_Invalid_SubTable;
+ goto Fail2;
+ }
+
+ FORGET_Frame();
+
+ s->LangSysRecord = NULL;
+
+ if ( ALLOC_ARRAY( s->LangSysRecord, count, TTO_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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ for ( n = 0; n < count; n++ )
+ Free_LangSys( &lsr[n].LangSys, memory );
+
+ FREE( s->LangSysRecord );
+
+ Fail2:
+ Free_LangSys( &s->DefaultLangSys, memory );
+ return error;
+ }
+
+
+ static void Free_Script( TTO_Script* s,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_LangSysRecord* lsr;
+
+
+ Free_LangSys( &s->DefaultLangSys, memory );
+
+ if ( s->LangSysRecord )
+ {
+ count = s->LangSysCount;
+ lsr = s->LangSysRecord;
+
+ for ( n = 0; n < count; n++ )
+ Free_LangSys( &lsr[n].LangSys, memory );
+
+ FREE( lsr );
+ }
+ }
+
+
+ /* ScriptList */
+
+ FT_Error Load_ScriptList( TTO_ScriptList* sl,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_ScriptRecord* sr;
+
+
+ base_offset = FILE_Pos();
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = sl->ScriptCount = GET_UShort();
+
+ FORGET_Frame();
+
+ sl->ScriptRecord = NULL;
+
+ if ( ALLOC_ARRAY( sl->ScriptRecord, count, TTO_ScriptRecord ) )
+ return error;
+
+ sr = sl->ScriptRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 6L ) )
+ goto Fail;
+
+ sr[n].ScriptTag = GET_ULong();
+ new_offset = GET_UShort() + base_offset;
+
+ FORGET_Frame();
+
+ cur_offset = FILE_Pos();
+ if ( FILE_Seek( new_offset ) ||
+ ( error = Load_Script( &sr[n].Script, stream ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_Script( &sr[n].Script, memory );
+
+ FREE( sl->ScriptRecord );
+ return error;
+ }
+
+
+ void Free_ScriptList( TTO_ScriptList* sl,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_ScriptRecord* sr;
+
+
+ if ( sl->ScriptRecord )
+ {
+ count = sl->ScriptCount;
+ sr = sl->ScriptRecord;
+
+ for ( n = 0; n < count; n++ )
+ Free_Script( &sr[n].Script, memory );
+
+ FREE( sr );
+ }
+ }
+
+
+
+ /*********************************
+ * Feature List related functions
+ *********************************/
+
+
+ /* Feature */
+
+ static FT_Error Load_Feature( TTO_Feature* f,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ FT_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, FT_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 TT_Err_Ok;
+ }
+
+
+ static void Free_Feature( TTO_Feature* f,
+ FT_Memory memory )
+ {
+ FREE( f->LookupListIndex );
+ }
+
+
+ /* FeatureList */
+
+ FT_Error Load_FeatureList( TTO_FeatureList* fl,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_FeatureRecord ) )
+ return error;
+
+ fr = fl->FeatureRecord;
+
+ for ( n = 0; n < count; n++ )
+ {
+ if ( ACCESS_Frame( 6L ) )
+ goto Fail;
+
+ 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 ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_Feature( &fr[n].Feature, memory );
+
+ FREE( fl->FeatureRecord );
+ return error;
+ }
+
+
+ void Free_FeatureList( TTO_FeatureList* fl,
+ FT_Memory memory)
+ {
+ FT_UShort n, count;
+
+ TTO_FeatureRecord* fr;
+
+
+ if ( fl->FeatureRecord )
+ {
+ count = fl->FeatureCount;
+ fr = fl->FeatureRecord;
+
+ for ( n = 0; n < count; n++ )
+ Free_Feature( &fr[n].Feature, memory );
+
+ FREE( fr );
+ }
+ }
+
+
+
+ /********************************
+ * Lookup List related functions
+ ********************************/
+
+ /* the subroutines of the following two functions are defined in
+ ftxgsub.c and ftxgpos.c respectively */
+
+
+ /* SubTable */
+
+ static FT_Error Load_SubTable( TTO_SubTable* st,
+ FT_Stream stream,
+ TTO_Type table_type,
+ FT_UShort lookup_type )
+ {
+ if ( table_type == GSUB )
+ switch ( lookup_type )
+ {
+ case GSUB_LOOKUP_SINGLE:
+ return Load_SingleSubst( &st->st.gsub.single, stream );
+
+ case GSUB_LOOKUP_MULTIPLE:
+ return Load_MultipleSubst( &st->st.gsub.multiple, stream );
+
+ case GSUB_LOOKUP_ALTERNATE:
+ return Load_AlternateSubst( &st->st.gsub.alternate, stream );
+
+ case GSUB_LOOKUP_LIGATURE:
+ return Load_LigatureSubst( &st->st.gsub.ligature, stream );
+
+ case GSUB_LOOKUP_CONTEXT:
+ return Load_ContextSubst( &st->st.gsub.context, stream );
+
+ case GSUB_LOOKUP_CHAIN:
+ return Load_ChainContextSubst( &st->st.gsub.chain, stream );
+
+ default:
+ return TTO_Err_Invalid_GSUB_SubTable_Format;
+ }
+ else
+ switch ( lookup_type )
+ {
+#if 0
+ case GPOS_LOOKUP_SINGLE:
+ return Load_SinglePos( &st->st.gpos.single, stream );
+
+ case GPOS_LOOKUP_PAIR:
+ return Load_PairPos( &st->st.gpos.pair, stream );
+
+ case GPOS_LOOKUP_CURSIVE:
+ return Load_CursivePos( &st->st.gpos.cursive, stream );
+
+ case GPOS_LOOKUP_MARKBASE:
+ return Load_MarkBasePos( &st->st.gpos.markbase, stream );
+
+ case GPOS_LOOKUP_MARKLIG:
+ return Load_MarkLigPos( &st->st.gpos.marklig, stream );
+
+ case GPOS_LOOKUP_MARKMARK:
+ return Load_MarkMarkPos( &st->st.gpos.markmark, stream );
+
+ case GPOS_LOOKUP_CONTEXT:
+ return Load_ContextPos( &st->st.gpos.context, stream );
+
+ case GPOS_LOOKUP_CHAIN:
+ return Load_ChainContextPos( &st->st.gpos.chain, stream );
+
+#endif
+ default:
+ return TTO_Err_Invalid_GPOS_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+ static void Free_SubTable( TTO_SubTable* st,
+ TTO_Type table_type,
+ FT_UShort lookup_type,
+ FT_Memory memory )
+ {
+ if ( table_type == GSUB )
+ switch ( lookup_type )
+ {
+ case GSUB_LOOKUP_SINGLE:
+ Free_SingleSubst( &st->st.gsub.single, memory );
+ break;
+
+ case GSUB_LOOKUP_MULTIPLE:
+ Free_MultipleSubst( &st->st.gsub.multiple, memory );
+ break;
+
+ case GSUB_LOOKUP_ALTERNATE:
+ Free_AlternateSubst( &st->st.gsub.alternate, memory );
+ break;
+
+ case GSUB_LOOKUP_LIGATURE:
+ Free_LigatureSubst( &st->st.gsub.ligature, memory );
+ break;
+
+ case GSUB_LOOKUP_CONTEXT:
+ Free_ContextSubst( &st->st.gsub.context, memory );
+ break;
+
+ case GSUB_LOOKUP_CHAIN:
+ Free_ChainContextSubst( &st->st.gsub.chain, memory );
+ break;
+ }
+ else
+ switch ( lookup_type )
+ {
+#if 0
+ case GPOS_LOOKUP_SINGLE:
+ Free_SinglePos( &st->st.gpos.single );
+ break;
+
+ case GPOS_LOOKUP_PAIR:
+ Free_PairPos( &st->st.gpos.pair );
+ break;
+
+ case GPOS_LOOKUP_CURSIVE:
+ Free_CursivePos( &st->st.gpos.cursive );
+ break;
+
+ case GPOS_LOOKUP_MARKBASE:
+ Free_MarkBasePos( &st->st.gpos.markbase );
+ break;
+
+ case GPOS_LOOKUP_MARKLIG:
+ Free_MarkLigPos( &st->st.gpos.marklig );
+ break;
+
+ case GPOS_LOOKUP_MARKMARK:
+ Free_MarkMarkPos( &st->st.gpos.markmark );
+ break;
+
+ case GPOS_LOOKUP_CONTEXT:
+ Free_ContextPos( &st->st.gpos.context );
+ break;
+
+ case GPOS_LOOKUP_CHAIN:
+ Free_ChainContextPos ( &st->st.gpos.chain );
+ break;
+#endif
+ }
+ }
+
+
+ /* Lookup */
+
+ static FT_Error Load_Lookup( TTO_Lookup* l,
+ FT_Stream stream,
+ TTO_Type type )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_SubTable* st;
+
+
+ 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, TTO_SubTable ) )
+ return error;
+
+ st = l->SubTable;
+
+ 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_SubTable( &st[n], stream,
+ type, l->LookupType ) ) != TT_Err_Ok )
+ goto Fail;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail:
+ for ( n = 0; n < count; n++ )
+ Free_SubTable( &st[n], type, l->LookupType, memory );
+
+ FREE( l->SubTable );
+ return error;
+ }
+
+
+ static void Free_Lookup( TTO_Lookup* l,
+ TTO_Type type,
+ FT_Memory memory)
+ {
+ FT_UShort n, count;
+
+ TTO_SubTable* st;
+
+
+ if ( l->SubTable )
+ {
+ count = l->SubTableCount;
+ st = l->SubTable;
+
+ for ( n = 0; n < count; n++ )
+ Free_SubTable( &st[n], type, l->LookupType, memory );
+
+ FREE( st );
+ }
+ }
+
+
+ /* LookupList */
+
+ FT_Error Load_LookupList( TTO_LookupList* ll,
+ FT_Stream stream,
+ TTO_Type type )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+ FT_ULong cur_offset, new_offset, base_offset;
+
+ TTO_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, TTO_Lookup ) )
+ return error;
+ if ( ALLOC_ARRAY( ll->Properties, count, FT_UShort ) )
+ 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 ) ) != TT_Err_Ok )
+ goto Fail1;
+ (void)FILE_Seek( cur_offset );
+ }
+
+ return TT_Err_Ok;
+
+ Fail1:
+ FREE( ll->Properties );
+
+ for ( n = 0; n < count; n++ )
+ Free_Lookup( &l[n], type, memory );
+
+ Fail2:
+ FREE( ll->Lookup );
+ return error;
+ }
+
+
+ void Free_LookupList( TTO_LookupList* ll,
+ TTO_Type type,
+ FT_Memory memory )
+ {
+ FT_UShort n, count;
+
+ TTO_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, memory );
+
+ FREE( l );
+ }
+ }
+
+
+
+ /*****************************
+ * Coverage related functions
+ *****************************/
+
+
+ /* CoverageFormat1 */
+
+ static FT_Error Load_Coverage1( TTO_CoverageFormat1* cf1,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ FT_UShort* ga;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = cf1->GlyphCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cf1->GlyphArray = NULL;
+
+ if ( ALLOC_ARRAY( cf1->GlyphArray, count, FT_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 TT_Err_Ok;
+ }
+
+
+ static void Free_Coverage1( TTO_CoverageFormat1* cf1,
+ FT_Memory memory)
+ {
+ FREE( cf1->GlyphArray );
+ }
+
+
+ /* CoverageFormat2 */
+
+ static FT_Error Load_Coverage2( TTO_CoverageFormat2* cf2,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ TTO_RangeRecord* rr;
+
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = cf2->RangeCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cf2->RangeRecord = NULL;
+
+ if ( ALLOC_ARRAY( cf2->RangeRecord, count, TTO_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 = TTO_Err_Invalid_SubTable;
+ goto Fail;
+ }
+ }
+
+ FORGET_Frame();
+
+ return TT_Err_Ok;
+
+ Fail:
+ FREE( cf2->RangeRecord );
+ return error;
+ }
+
+
+ static void Free_Coverage2( TTO_CoverageFormat2* cf2,
+ FT_Memory memory )
+ {
+ FREE( cf2->RangeRecord );
+ }
+
+
+ FT_Error Load_Coverage( TTO_Coverage* c,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ 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 TTO_Err_Invalid_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+ void Free_Coverage( TTO_Coverage* c,
+ FT_Memory memory )
+ {
+ switch ( c->CoverageFormat )
+ {
+ case 1:
+ Free_Coverage1( &c->cf.cf1, memory );
+ break;
+
+ case 2:
+ Free_Coverage2( &c->cf.cf2, memory );
+ break;
+ }
+ }
+
+
+ static FT_Error Coverage_Index1( TTO_CoverageFormat1* cf1,
+ FT_UShort glyphID,
+ FT_UShort* index )
+ {
+ FT_UShort min, max, new_min, new_max, middle;
+
+ FT_UShort* array = cf1->GlyphArray;
+
+
+ /* binary search */
+
+ 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 TT_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 TTO_Err_Not_Covered;
+ }
+
+
+ static FT_Error Coverage_Index2( TTO_CoverageFormat2* cf2,
+ FT_UShort glyphID,
+ FT_UShort* index )
+ {
+ FT_UShort min, max, new_min, new_max, middle;
+
+ TTO_RangeRecord* rr = cf2->RangeRecord;
+
+
+ /* binary search */
+
+ 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 TT_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 TTO_Err_Not_Covered;
+ }
+
+
+ FT_Error Coverage_Index( TTO_Coverage* c,
+ FT_UShort glyphID,
+ FT_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 TTO_Err_Invalid_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+
+ /*************************************
+ * Class Definition related functions
+ *************************************/
+
+
+ /* ClassDefFormat1 */
+
+ static FT_Error Load_ClassDef1( TTO_ClassDefinition* cd,
+ FT_UShort limit,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ FT_UShort* cva;
+ FT_Bool* d;
+
+ TTO_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 TTO_Err_Invalid_SubTable;
+
+ cdf1->ClassValueArray = NULL;
+
+ if ( ALLOC_ARRAY( cdf1->ClassValueArray, count, FT_UShort ) )
+ return error;
+
+ d = cd->Defined;
+ 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 = TTO_Err_Invalid_SubTable;
+ goto Fail;
+ }
+ d[cva[n]] = TRUE;
+ }
+
+ FORGET_Frame();
+
+ return TT_Err_Ok;
+
+ Fail:
+ FREE( cva );
+
+ return error;
+ }
+
+
+ static void Free_ClassDef1( TTO_ClassDefFormat1* cdf1,
+ FT_Memory memory )
+ {
+ FREE( cdf1->ClassValueArray );
+ }
+
+
+ /* ClassDefFormat2 */
+
+ static FT_Error Load_ClassDef2( TTO_ClassDefinition* cd,
+ FT_UShort limit,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ TTO_ClassRangeRecord* crr;
+ FT_Bool* d;
+
+ TTO_ClassDefFormat2* cdf2;
+
+
+ cdf2 = &cd->cd.cd2;
+
+ if ( ACCESS_Frame( 2L ) )
+ return error;
+
+ count = cdf2->ClassRangeCount = GET_UShort();
+
+ FORGET_Frame();
+
+ cdf2->ClassRangeRecord = NULL;
+
+ if ( ALLOC_ARRAY( cdf2->ClassRangeRecord, count, TTO_ClassRangeRecord ) )
+ return error;
+
+ d = cd->Defined;
+ 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 )
+ {
+ error = TTO_Err_Invalid_SubTable;
+ goto Fail;
+ }
+ d[crr[n].Class] = TRUE;
+ }
+
+ FORGET_Frame();
+
+ return TT_Err_Ok;
+
+ Fail:
+ FREE( crr );
+
+ return error;
+ }
+
+
+ static void Free_ClassDef2( TTO_ClassDefFormat2* cdf2,
+ FT_Memory memory )
+ {
+ FREE( cdf2->ClassRangeRecord );
+ }
+
+
+ /* ClassDefinition */
+
+ FT_Error Load_ClassDefinition( TTO_ClassDefinition* cd,
+ FT_UShort limit,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+
+ if ( ALLOC_ARRAY( cd->Defined, limit, FT_Bool ) )
+ return error;
+
+ if ( ACCESS_Frame( 2L ) )
+ goto Fail;
+
+ 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 = TTO_Err_Invalid_SubTable_Format;
+ break;
+ }
+
+ if ( error )
+ goto Fail;
+
+ cd->loaded = TRUE;
+
+ return TT_Err_Ok;
+
+ Fail:
+ FREE( cd->Defined );
+ return error;
+ }
+
+
+ void Free_ClassDefinition( TTO_ClassDefinition* cd,
+ FT_Memory memory )
+ {
+ if ( !cd->loaded )
+ return;
+
+ FREE( cd->Defined );
+
+ switch ( cd->ClassFormat )
+ {
+ case 1:
+ Free_ClassDef1( &cd->cd.cd1, memory );
+ break;
+
+ case 2:
+ Free_ClassDef2( &cd->cd.cd2, memory );
+ break;
+ }
+ }
+
+
+ static FT_Error Get_Class1( TTO_ClassDefFormat1* cdf1,
+ FT_UShort glyphID,
+ FT_UShort* class,
+ FT_UShort* index )
+ {
+ FT_UShort* cva = cdf1->ClassValueArray;
+
+
+ *index = 0;
+
+ if ( glyphID >= cdf1->StartGlyph &&
+ glyphID <= cdf1->StartGlyph + cdf1->GlyphCount )
+ {
+ *class = cva[glyphID - cdf1->StartGlyph];
+ return TT_Err_Ok;
+ }
+ else
+ {
+ *class = 0;
+ return TTO_Err_Not_Covered;
+ }
+ }
+
+
+ /* we need the index value of the last searched class range record
+ in case of failure for constructed GDEF tables */
+
+ static FT_Error Get_Class2( TTO_ClassDefFormat2* cdf2,
+ FT_UShort glyphID,
+ FT_UShort* class,
+ FT_UShort* index )
+ {
+ FT_Error error = TT_Err_Ok;
+ FT_UShort min, max, new_min, new_max, middle;
+
+ TTO_ClassRangeRecord* crr = cdf2->ClassRangeRecord;
+
+
+ /* binary search */
+
+ 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 )
+ {
+ *class = crr[middle].Class;
+ error = TT_Err_Ok;
+ break;
+ }
+ else if ( glyphID < crr[middle].Start )
+ {
+ if ( middle == min )
+ {
+ *class = 0;
+ error = TTO_Err_Not_Covered;
+ break;
+ }
+ new_max = middle - 1;
+ }
+ else
+ {
+ if ( middle == max )
+ {
+ *class = 0;
+ error = TTO_Err_Not_Covered;
+ break;
+ }
+ new_min = middle + 1;
+ }
+ } while ( min < max );
+
+ if ( index )
+ *index = middle;
+
+ return error;
+ }
+
+
+ FT_Error Get_Class( TTO_ClassDefinition* cd,
+ FT_UShort glyphID,
+ FT_UShort* class,
+ FT_UShort* index )
+ {
+ switch ( cd->ClassFormat )
+ {
+ case 1:
+ return Get_Class1( &cd->cd.cd1, glyphID, class, index );
+
+ case 2:
+ return Get_Class2( &cd->cd.cd2, glyphID, class, index );
+
+ default:
+ return TTO_Err_Invalid_SubTable_Format;
+ }
+
+ return TT_Err_Ok; /* never reached */
+ }
+
+
+
+ /***************************
+ * Device related functions
+ ***************************/
+
+
+ FT_Error Load_Device( TTO_Device* d,
+ FT_Stream stream )
+ {
+ FT_Error error;
+ FT_Memory memory = stream->memory;
+
+ FT_UShort n, count;
+
+ FT_UShort* dv;
+
+
+ if ( ACCESS_Frame( 6L ) )
+ return error;
+
+ d->StartSize = GET_UShort();
+ d->EndSize = GET_UShort();
+ d->DeltaFormat = GET_UShort();
+
+ FORGET_Frame();
+
+ if ( d->StartSize > d->EndSize ||
+ d->DeltaFormat == 0 || d->DeltaFormat > 3 )
+ return TTO_Err_Invalid_SubTable;
+
+ d->DeltaValue = NULL;
+
+ count = ( ( d->EndSize - d->StartSize + 1 ) >>
+ ( 4 - d->DeltaFormat ) ) + 1;
+
+ if ( ALLOC_ARRAY( d->DeltaValue, count, FT_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 TT_Err_Ok;
+ }
+
+
+ void Free_Device( TTO_Device* d,
+ FT_Memory memory )
+ {
+ 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 */
+
+ FT_Error Get_Device( TTO_Device* d,
+ FT_UShort size,
+ FT_Short* value )
+ {
+ FT_UShort byte, bits, mask, f, s;
+
+
+ f = d->DeltaFormat;
+
+ if ( 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 = (FT_Short)( bits & mask );
+
+ /* conversion to a signed value */
+
+ if ( *value >= ( ( mask + 1 ) >> 1 ) )
+ *value -= mask + 1;
+
+ return TT_Err_Ok;
+ }
+ else
+ {
+ *value = 0;
+ return TTO_Err_Not_Covered;
+ }
+ }
+
+
+/* END */
diff --git a/src/ftxopen.h b/src/ftxopen.h
new file mode 100644
index 00000000..28dff902
--- /dev/null
+++ b/src/ftxopen.h
@@ -0,0 +1,308 @@
+/*******************************************************************
+ *
+ * ftxopen.h
+ *
+ * TrueType Open support.
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ * This file should be included by the application. Nevertheless,
+ * the table specific APIs (and structures) are located in files like
+ * ftxgsub.h or ftxgpos.h; these header files are read by ftxopen.h .
+ *
+ ******************************************************************/
+
+#ifndef FTXOPEN_H
+#define FTXOPEN_H
+
+#include <freetype/freetype.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EXPORT_DEF
+#define EXPORT_FUNC
+
+#define TTO_MAX_NESTING_LEVEL 100
+
+#define TTO_Err_Invalid_SubTable_Format 0x1000
+#define TTO_Err_Invalid_SubTable 0x1001
+#define TTO_Err_Not_Covered 0x1002
+#define TTO_Err_Too_Many_Nested_Contexts 0x1003
+#define TTO_Err_No_MM_Interpreter 0x1004
+
+
+ /* Script list related structures */
+
+ struct TTO_LangSys_
+ {
+ FT_UShort LookupOrderOffset; /* always 0 for TT Open 1.0 */
+ FT_UShort ReqFeatureIndex; /* required FeatureIndex */
+ FT_UShort FeatureCount; /* number of Feature indices */
+ FT_UShort* FeatureIndex; /* array of Feature indices */
+ };
+
+ typedef struct TTO_LangSys_ TTO_LangSys;
+
+
+ struct TTO_LangSysRecord_
+ {
+ FT_ULong LangSysTag; /* LangSysTag identifier */
+ TTO_LangSys LangSys; /* LangSys table */
+ };
+
+ typedef struct TTO_LangSysRecord_ TTO_LangSysRecord;
+
+
+ struct TTO_Script_
+ {
+ TTO_LangSys DefaultLangSys; /* DefaultLangSys table */
+ FT_UShort LangSysCount; /* number of LangSysRecords */
+ TTO_LangSysRecord* LangSysRecord; /* array of LangSysRecords */
+ };
+
+ typedef struct TTO_Script_ TTO_Script;
+
+
+ struct TTO_ScriptRecord_
+ {
+ FT_ULong ScriptTag; /* ScriptTag identifier */
+ TTO_Script Script; /* Script table */
+ };
+
+ typedef struct TTO_ScriptRecord_ TTO_ScriptRecord;
+
+
+ struct TTO_ScriptList_
+ {
+ FT_UShort ScriptCount; /* number of ScriptRecords */
+ TTO_ScriptRecord* ScriptRecord; /* array of ScriptRecords */
+ };
+
+ typedef struct TTO_ScriptList_ TTO_ScriptList;
+
+
+ /* Feature list related structures */
+
+ struct TTO_Feature_
+ {
+ FT_UShort FeatureParams; /* always 0 for TT Open 1.0 */
+ FT_UShort LookupListCount; /* number of LookupList indices */
+ FT_UShort* LookupListIndex; /* array of LookupList indices */
+ };
+
+ typedef struct TTO_Feature_ TTO_Feature;
+
+
+ struct TTO_FeatureRecord_
+ {
+ FT_ULong FeatureTag; /* FeatureTag identifier */
+ TTO_Feature Feature; /* Feature table */
+ };
+
+ typedef struct TTO_FeatureRecord_ TTO_FeatureRecord;
+
+
+ struct TTO_FeatureList_
+ {
+ FT_UShort FeatureCount; /* number of FeatureRecords */
+ TTO_FeatureRecord* FeatureRecord; /* array of FeatureRecords */
+ };
+
+ typedef struct TTO_FeatureList_ TTO_FeatureList;
+
+
+ /* Lookup list related structures */
+
+ struct TTO_SubTable_; /* defined below after inclusion
+ of ftxgsub.h and ftxgpos.h */
+ typedef struct TTO_SubTable_ TTO_SubTable;
+
+
+ struct TTO_Lookup_
+ {
+ FT_UShort LookupType; /* Lookup type */
+ FT_UShort LookupFlag; /* Lookup qualifiers */
+ FT_UShort SubTableCount; /* number of SubTables */
+ TTO_SubTable* SubTable; /* array of SubTables */
+ };
+
+ typedef struct TTO_Lookup_ TTO_Lookup;
+
+
+ /* The `Properties' field is not defined in the TTO specification but
+ is needed for processing lookups. If properties[n] is > 0, the
+ functions TT_GSUB_Apply_String() resp. TT_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 TTO_LookupList_
+ {
+ FT_UShort LookupCount; /* number of Lookups */
+ TTO_Lookup* Lookup; /* array of Lookup records */
+ FT_UShort* Properties; /* array of flags */
+ };
+
+ typedef struct TTO_LookupList_ TTO_LookupList;
+
+
+/* Possible LookupFlag bit masks. `IGNORE_SPECIAL_MARKS' comes from the
+ OpenType 1.2 specification. */
+
+#define IGNORE_BASE_GLYPHS 0x0002
+#define IGNORE_LIGATURES 0x0004
+#define IGNORE_MARKS 0x0008
+#define IGNORE_SPECIAL_MARKS 0xFF00
+
+
+ struct TTO_CoverageFormat1_
+ {
+ FT_UShort GlyphCount; /* number of glyphs in GlyphArray */
+ FT_UShort* GlyphArray; /* array of glyph IDs */
+ };
+
+ typedef struct TTO_CoverageFormat1_ TTO_CoverageFormat1;
+
+
+ struct TTO_RangeRecord_
+ {
+ FT_UShort Start; /* first glyph ID in the range */
+ FT_UShort End; /* last glyph ID in the range */
+ FT_UShort StartCoverageIndex; /* coverage index of first
+ glyph ID in the range */
+ };
+
+ typedef struct TTO_RangeRecord_ TTO_RangeRecord;
+
+
+ struct TTO_CoverageFormat2_
+ {
+ FT_UShort RangeCount; /* number of RangeRecords */
+ TTO_RangeRecord* RangeRecord; /* array of RangeRecords */
+ };
+
+ typedef struct TTO_CoverageFormat2_ TTO_CoverageFormat2;
+
+
+ struct TTO_Coverage_
+ {
+ FT_UShort CoverageFormat; /* 1 or 2 */
+
+ union
+ {
+ TTO_CoverageFormat1 cf1;
+ TTO_CoverageFormat2 cf2;
+ } cf;
+ };
+
+ typedef struct TTO_Coverage_ TTO_Coverage;
+
+
+ struct TTO_ClassDefFormat1_
+ {
+ FT_UShort StartGlyph; /* first glyph ID of the
+ ClassValueArray */
+ FT_UShort GlyphCount; /* size of the ClassValueArray */
+ FT_UShort* ClassValueArray; /* array of class values */
+ };
+
+ typedef struct TTO_ClassDefFormat1_ TTO_ClassDefFormat1;
+
+
+ struct TTO_ClassRangeRecord_
+ {
+ FT_UShort Start; /* first glyph ID in the range */
+ FT_UShort End; /* last glyph ID in the range */
+ FT_UShort Class; /* applied to all glyphs in range */
+ };
+
+ typedef struct TTO_ClassRangeRecord_ TTO_ClassRangeRecord;
+
+
+ struct TTO_ClassDefFormat2_
+ {
+ FT_UShort ClassRangeCount;
+ /* number of ClassRangeRecords */
+ TTO_ClassRangeRecord* ClassRangeRecord;
+ /* array of ClassRangeRecords */
+ };
+
+ typedef struct TTO_ClassDefFormat2_ TTO_ClassDefFormat2;
+
+
+ /* The `Defined' field is not defined in the TTO specification but
+ apparently needed for processing fonts like trado.ttf: This font
+ refers to a class which contains not a single element. We map such
+ classes to class 0. */
+
+ struct TTO_ClassDefinition_
+ {
+ FT_Bool loaded;
+
+ FT_Bool* Defined; /* array of Booleans.
+ If Defined[n] is FALSE,
+ class n contains no glyphs. */
+ FT_UShort ClassFormat; /* 1 or 2 */
+
+ union
+ {
+ TTO_ClassDefFormat1 cd1;
+ TTO_ClassDefFormat2 cd2;
+ } cd;
+ };
+
+ typedef struct TTO_ClassDefinition_ TTO_ClassDefinition;
+
+
+ struct TTO_Device_
+ {
+ FT_UShort StartSize; /* smallest size to correct */
+ FT_UShort EndSize; /* largest size to correct */
+ FT_UShort DeltaFormat; /* DeltaValue array data format:
+ 1, 2, or 3 */
+ FT_UShort* DeltaValue; /* array of compressed data */
+ };
+
+ typedef struct TTO_Device_ TTO_Device;
+
+
+#include "ftxgdef.h"
+#include "ftxgsub.h"
+#include "ftxgpos.h"
+
+
+ struct TTO_SubTable_
+ {
+ union
+ {
+ TTO_GSUB_SubTable gsub;
+ TTO_GPOS_SubTable gpos;
+ } st;
+ };
+
+
+ enum TTO_Type_
+ {
+ GSUB,
+ GPOS
+ };
+
+ typedef enum TTO_Type_ TTO_Type;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FTXOPEN_H */
+
+
+/* END */
diff --git a/src/ftxopenf.h b/src/ftxopenf.h
new file mode 100644
index 00000000..bbb2c851
--- /dev/null
+++ b/src/ftxopenf.h
@@ -0,0 +1,161 @@
+/*******************************************************************
+ *
+ * ftxopenf.h
+ *
+ * internal TrueType Open functions
+ *
+ * Copyright 1996-2000 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used
+ * modified and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT. By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ ******************************************************************/
+
+#ifndef FTXOPENF_H
+#define FTXOPENF_H
+
+#include "ftxopen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ /* functions from ftxopen.c */
+
+ FT_Error Load_ScriptList( TTO_ScriptList* sl,
+ FT_Stream stream );
+ FT_Error Load_FeatureList( TTO_FeatureList* fl,
+ FT_Stream input );
+ FT_Error Load_LookupList( TTO_LookupList* ll,
+ FT_Stream input,
+ TTO_Type type );
+
+ FT_Error Load_Coverage( TTO_Coverage* c,
+ FT_Stream input );
+ FT_Error Load_ClassDefinition( TTO_ClassDefinition* cd,
+ FT_UShort limit,
+ FT_Stream input );
+ FT_Error Load_Device( TTO_Device* d,
+ FT_Stream input );
+
+ void Free_ScriptList( TTO_ScriptList* sl,
+ FT_Memory memory );
+ void Free_FeatureList( TTO_FeatureList* fl,
+ FT_Memory memory );
+ void Free_LookupList( TTO_LookupList* ll,
+ TTO_Type type,
+ FT_Memory memory );
+
+ void Free_Coverage( TTO_Coverage* c,
+ FT_Memory memory );
+ void Free_ClassDefinition( TTO_ClassDefinition* cd,
+ FT_Memory memory );
+ void Free_Device( TTO_Device* d,
+ FT_Memory memory );
+
+
+ /* functions from ftxgsub.c */
+
+ FT_Error Load_SingleSubst( TTO_SingleSubst* ss,
+ FT_Stream input );
+ FT_Error Load_MultipleSubst( TTO_MultipleSubst* ms,
+ FT_Stream input );
+ FT_Error Load_AlternateSubst( TTO_AlternateSubst* as,
+ FT_Stream input );
+ FT_Error Load_LigatureSubst( TTO_LigatureSubst* ls,
+ FT_Stream input );
+ FT_Error Load_ContextSubst( TTO_ContextSubst* cs,
+ FT_Stream input );
+ FT_Error Load_ChainContextSubst( TTO_ChainContextSubst* ccs,
+ FT_Stream input );
+
+ void Free_SingleSubst( TTO_SingleSubst* ss,
+ FT_Memory memory );
+ void Free_MultipleSubst( TTO_MultipleSubst* ms,
+ FT_Memory memory );
+ void Free_AlternateSubst( TTO_AlternateSubst* as,
+ FT_Memory memory );
+ void Free_LigatureSubst( TTO_LigatureSubst* ls,
+ FT_Memory memory );
+ void Free_ContextSubst( TTO_ContextSubst* cs,
+ FT_Memory memory );
+ void Free_ChainContextSubst( TTO_ChainContextSubst* ccs,
+ FT_Memory memory );
+
+
+ /* functions from ftxgpos.c */
+
+ FT_Error Load_SinglePos( TTO_SinglePos* sp,
+ FT_Stream input );
+ FT_Error Load_PairPos( TTO_PairPos* pp,
+ FT_Stream input );
+ FT_Error Load_CursivePos( TTO_CursivePos* cp,
+ FT_Stream input );
+ FT_Error Load_MarkBasePos( TTO_MarkBasePos* mbp,
+ FT_Stream input );
+ FT_Error Load_MarkLigPos( TTO_MarkLigPos* mlp,
+ FT_Stream input );
+ FT_Error Load_MarkMarkPos( TTO_MarkMarkPos* mmp,
+ FT_Stream input );
+ FT_Error Load_ContextPos( TTO_ContextPos* cp,
+ FT_Stream input );
+ FT_Error Load_ChainContextPos( TTO_ChainContextPos* ccp,
+ FT_Stream input );
+
+ void Free_SinglePos( TTO_SinglePos* sp,
+ FT_Memory memory );
+ void Free_PairPos( TTO_PairPos* pp,
+ FT_Memory memory );
+ void Free_CursivePos( TTO_CursivePos* cp,
+ FT_Memory memory );
+ void Free_MarkBasePos( TTO_MarkBasePos* mbp,
+ FT_Memory memory );
+ void Free_MarkLigPos( TTO_MarkLigPos* mlp,
+ FT_Memory memory );
+ void Free_MarkMarkPos( TTO_MarkMarkPos* mmp,
+ FT_Memory memory );
+ void Free_ContextPos( TTO_ContextPos* cp,
+ FT_Memory memory );
+ void Free_ChainContextPos( TTO_ChainContextPos* ccp,
+ FT_Memory memory );
+ /* query functions */
+
+ FT_Error Coverage_Index( TTO_Coverage* c,
+ FT_UShort glyphID,
+ FT_UShort* index );
+ FT_Error Get_Class( TTO_ClassDefinition* cd,
+ FT_UShort glyphID,
+ FT_UShort* class,
+ FT_UShort* index );
+ FT_Error Get_Device( TTO_Device* d,
+ FT_UShort size,
+ FT_Short* value );
+
+
+ /* functions from ftxgdef.c */
+
+ FT_Error Add_Glyph_Property( TTO_GDEFHeader* gdef,
+ FT_UShort glyphID,
+ FT_UShort property );
+
+ FT_Error Check_Property( TTO_GDEFHeader* gdef,
+ FT_UShort index,
+ FT_UShort flags,
+ FT_UShort* property );
+
+#define CHECK_Property( gdef, index, flags, property ) \
+ ( ( error = Check_Property( (gdef), (index), (flags), \
+ (property) ) ) != TT_Err_Ok )
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FTXOPENF_H */
+
+
+/* END */
diff --git a/src/ottest.c b/src/ottest.c
new file mode 100644
index 00000000..c6338e96
--- /dev/null
+++ b/src/ottest.c
@@ -0,0 +1,265 @@
+/* Pango
+ * otttest.c: Test program for OpenType
+ *
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ftxopen.h"
+#include <freetype/internal/ftmemory.h>
+
+#include "disasm.h"
+
+#define N_ELEMENTS(arr) (sizeof(arr)/ sizeof((arr)[0]))
+
+int
+croak (const char *situation, FT_Error error)
+{
+ fprintf (stderr, "%s: Error %d\n", situation, error);
+
+ exit (1);
+}
+
+enum {
+ I = 1 << 0,
+ M = 1 << 1,
+ F = 1 << 2,
+ L = 1 << 3
+};
+
+void
+print_tag (FT_ULong tag)
+{
+ fprintf (stderr, "%c%c%c%c",
+ (unsigned char)(tag >> 24),
+ (unsigned char)((tag & 0xff0000) >> 16),
+ (unsigned char)((tag & 0xff00) >> 8),
+ (unsigned char)(tag & 0xff));
+}
+
+void
+maybe_add_feature (TTO_GSUB gsub,
+ FT_UShort script_index,
+ FT_ULong tag,
+ FT_UShort property)
+{
+ FT_Error error;
+ FT_UShort feature_index;
+
+ /* 0xffff == default language system */
+ error = TT_GSUB_Select_Feature (gsub, tag, script_index, 0xffff, &feature_index);
+
+ if (error)
+ {
+ if (error == TTO_Err_Not_Covered)
+ {
+ print_tag (tag);
+ fprintf (stderr, " not covered, ignored\n");
+ return;
+ }
+
+ croak ("TT_GSUB_Select_Feature", error);
+ }
+
+ if ((error = TT_GSUB_Add_Feature (gsub, feature_index, property)))
+ croak ("TT_GSUB_Add_Feature", error);
+}
+
+void
+select_cmap (FT_Face face)
+{
+ FT_UShort i;
+ FT_CharMap cmap = NULL;
+
+ for (i = 0; i < face->num_charmaps; i++)
+ {
+ if (face->charmaps[i]->platform_id == 3 && face->charmaps[i]->encoding_id == 1)
+ {
+ cmap = face->charmaps[i];
+ break;
+ }
+ }
+
+ /* we try only pid/eid (0,0) if no (3,1) map is found -- many Windows
+ fonts have only rudimentary (0,0) support. */
+
+ if (!cmap)
+ for (i = 0; i < face->num_charmaps; i++)
+ {
+ if (face->charmaps[i]->platform_id == 3 && face->charmaps[i]->encoding_id == 1)
+ {
+ cmap = face->charmaps[i];
+ break;
+ }
+ }
+
+ if (cmap)
+ FT_Set_Charmap (face, cmap);
+ else
+ {
+ fprintf (stderr, "Sorry, but this font doesn't contain"
+ " any Unicode mapping table.\n");
+ exit (1);
+ }
+}
+
+void
+add_features (TTO_GSUB gsub)
+{
+ FT_Error error;
+ FT_ULong tag = FT_MAKE_TAG ('a', 'r', 'a', 'b');
+ FT_UShort script_index;
+
+ error = TT_GSUB_Select_Script (gsub, tag, &script_index);
+
+ if (error)
+ {
+ if (error == TTO_Err_Not_Covered)
+ {
+ fprintf (stderr, "Arabic not covered, no features used\n");
+ return;
+ }
+
+ croak ("TT_GSUB_Select_Script", error);
+ }
+
+ maybe_add_feature (gsub, script_index, FT_MAKE_TAG ('i', 'n', 'i', 't'), I);
+ maybe_add_feature (gsub, script_index, FT_MAKE_TAG ('m', 'e', 'd', 'i'), M);
+ maybe_add_feature (gsub, script_index, FT_MAKE_TAG ('f', 'i', 'n', 'a'), F);
+ maybe_add_feature (gsub, script_index, FT_MAKE_TAG ('l', 'i', 'g', 'a'), L);
+}
+
+void
+dump_string (TTO_GSUB_String *str)
+{
+ int i;
+
+ fprintf (stderr, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
+ for (i = 0; i < str->length; i++)
+ {
+ fprintf (stderr, "%2d: %#06x %#06x %4d %4d\n",
+ i,
+ str->string[i],
+ str->properties[i],
+ str->components[i],
+ str->ligIDs[i]);
+ }
+ fprintf (stderr, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n");
+}
+
+FT_UShort arabic_str[] = { 0x645, 0x643, 0x64a, 0x644, 0x639, 0x20, 0x645, 0x627, 0x644, 0x633, 0x644, 0x627 };
+FT_UShort arabic_props[] = { I|L, M|L, M|L, M|L, M|L, F|L, I|L, M|L, M|L, M|L, M|L, F|L };
+
+void
+try_string (FT_Library library,
+ FT_Face face,
+ TTO_GSUB gsub)
+{
+ FT_Error error;
+ TTO_GSUB_String *in_str;
+ TTO_GSUB_String *out_str;
+ int i;
+
+ if ((error = TT_GSUB_String_New (face->memory, &in_str)))
+ croak ("TT_GSUB_String_New", error);
+ if ((error = TT_GSUB_String_New (face->memory, &out_str)))
+ croak ("TT_GSUB_String_New", error);
+
+ if ((error = TT_GSUB_String_Set_Length (in_str, N_ELEMENTS (arabic_str))))
+ croak ("TT_GSUB_String_Set_Length", error);
+
+ for (i=0; i < N_ELEMENTS (arabic_str); i++)
+ {
+ in_str->string[i] = FT_Get_Char_Index (face, arabic_str[i]);
+ in_str->properties[i] = arabic_props[i];
+ in_str->components[i] = i;
+ in_str->ligIDs[i] = i;
+ }
+
+ if ((error = TT_GSUB_Apply_String (gsub, in_str, out_str)))
+ croak ("TT_GSUB_Apply_String", error);
+
+ dump_string (in_str);
+ dump_string (out_str);
+
+ if ((error = TT_GSUB_String_Done (in_str)))
+ croak ("TT_GSUB_String_New", error);
+ if ((error = TT_GSUB_String_Done (out_str)))
+ croak ("TT_GSUB_String_New", error);
+}
+
+int
+main (int argc, char **argv)
+{
+ FT_Error error;
+ FT_Library library;
+ FT_Face face;
+ TTO_GSUB gsub;
+ TTO_GPOS gpos;
+
+ if (argc != 2)
+ {
+ fprintf (stderr, "Usage: ottest 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, &face)))
+ croak ("FT_New_Face", error);
+
+ if (!(error = TT_Load_GSUB_Table (face, &gsub, NULL)))
+ {
+ TT_Dump_GSUB_Table (gsub, stdout);
+
+ if ((error = TT_Done_GSUB_Table (gsub)))
+ croak ("FT_Done_GSUB_Table", error);
+ }
+ else
+ fprintf (stderr, "TT_Load_GSUB_Table %d\n", error);
+
+ if (!(error = TT_Load_GPOS_Table (face, &gpos, NULL)))
+ {
+ TT_Dump_GPOS_Table (gpos, stdout);
+
+ if ((error = TT_Done_GPOS_Table (gpos)))
+ croak ("FT_Done_GPOS_Table", error);
+ }
+ else
+ fprintf (stderr, "TT_Load_GPOS_Table %d\n", error);
+
+#if 0
+ select_cmap (face);
+
+ add_features (gsub);
+ try_string (library, face, gsub);
+#endif
+
+
+ if ((error = FT_Done_Face (face)))
+ croak ("FT_Done_Face", error);
+
+ if ((error = FT_Done_FreeType (library)))
+ croak ("FT_Done_FreeType", error);
+
+ return 0;
+}
+
diff --git a/src/pango-ot-info.c b/src/pango-ot-info.c
new file mode 100644
index 00000000..b426e32e
--- /dev/null
+++ b/src/pango-ot-info.c
@@ -0,0 +1,438 @@
+/* Pango
+ * pango-ot-info.c: Store tables for OpenType
+ *
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "pango-ot-private.h"
+#include <freetype/internal/tterrors.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/ftmodule.h>
+
+static void pango_ot_info_class_init (GObjectClass *object_class);
+static void pango_ot_info_finalize (GObject *object);
+
+static GObjectClass *parent_class;
+
+enum
+{
+ INFO_LOADED_GDEF = 1 << 0,
+ INFO_LOADED_GSUB = 1 << 1,
+ INFO_LOADED_GPOS = 1 << 2
+};
+
+GType
+pango_ot_info_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (!object_type)
+ {
+ static const GTypeInfo object_info =
+ {
+ sizeof (PangoOTInfoClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc)pango_ot_info_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (PangoOTInfo),
+ 0, /* n_preallocs */
+ NULL /* init */
+ };
+
+ object_type = g_type_register_static (G_TYPE_OBJECT,
+ "PangoOTInfo",
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+static void
+pango_ot_info_class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_peek_parent (object_class);
+
+ object_class->finalize = pango_ot_info_finalize;
+}
+
+static void
+pango_ot_info_finalize (GObject *object)
+{
+ PangoOTInfo *info = PANGO_OT_INFO (object);
+
+ if (info->gdef)
+ {
+ TT_Done_GDEF_Table (info->gdef);
+ info->gdef = NULL;
+ }
+ if (info->gsub)
+ {
+ TT_Done_GSUB_Table (info->gsub);
+ info->gsub = NULL;
+ }
+ if (info->gpos)
+ {
+ TT_Done_GPOS_Table (info->gpos);
+ info->gpos = NULL;
+ }
+}
+
+PangoOTInfo *
+pango_ot_info_new (FT_Face face)
+{
+ PangoOTInfo *info;
+
+ info = g_object_new (PANGO_TYPE_OT_INFO, NULL);
+
+ info->face = face;
+
+ return info;
+}
+
+/* There must be be a better way to do this
+ */
+static gboolean
+is_truetype (FT_Face face)
+{
+ return strcmp (FT_MODULE_CLASS (face->driver)->module_name, "truetype") == 0;
+}
+
+TTO_GDEF
+pango_ot_info_get_gdef (PangoOTInfo *info)
+{
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
+
+ if (!(info->loaded & INFO_LOADED_GDEF))
+ {
+ FT_Error error;
+
+ info->loaded |= INFO_LOADED_GDEF;
+
+ if (is_truetype (info->face))
+ {
+ error = TT_Load_GDEF_Table (info->face, &info->gdef);
+
+ if (error && error != TT_Err_Table_Missing)
+ g_warning ("Error loading GDEF table %d", error);
+ }
+ }
+
+ return info->gdef;
+}
+
+TTO_GSUB
+pango_ot_info_get_gsub (PangoOTInfo *info)
+{
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
+
+ if (!(info->loaded & INFO_LOADED_GSUB))
+ {
+ FT_Error error;
+ TTO_GDEF gdef = pango_ot_info_get_gdef (info);
+
+ info->loaded |= INFO_LOADED_GSUB;
+
+ if (is_truetype (info->face))
+ {
+ error = TT_Load_GSUB_Table (info->face, &info->gsub, gdef);
+
+ if (error && error != TT_Err_Table_Missing)
+ g_warning ("Error loading GSUB table %d", error);
+ }
+ }
+
+ return info->gsub;
+}
+
+TTO_GPOS
+pango_ot_info_get_gpos (PangoOTInfo *info)
+{
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
+
+ if (!(info->loaded & INFO_LOADED_GPOS))
+ {
+ FT_Error error;
+ TTO_GDEF gdef = pango_ot_info_get_gdef (info);
+
+ info->loaded |= INFO_LOADED_GPOS;
+
+ if (is_truetype (info->face))
+ {
+ error = TT_Load_GPOS_Table (info->face, &info->gpos, gdef);
+
+ if (error && error != TT_Err_Table_Missing)
+ g_warning ("Error loading GPOS table %d", error);
+ }
+ }
+
+ return info->gpos;
+}
+
+static gboolean
+get_tables (PangoOTInfo *info,
+ PangoOTTableType table_type,
+ TTO_ScriptList **script_list,
+ TTO_FeatureList **feature_list)
+{
+ if (table_type == PANGO_OT_TABLE_GSUB)
+ {
+ TTO_GSUB gsub = pango_ot_info_get_gsub (info);
+
+ if (!gsub)
+ return FALSE;
+ else
+ {
+ if (script_list)
+ *script_list = &gsub->ScriptList;
+ if (feature_list)
+ *feature_list = &gsub->FeatureList;
+ return TRUE;
+ }
+ }
+ else
+ {
+ TTO_GPOS gpos = pango_ot_info_get_gpos (info);
+
+ if (!gpos)
+ return FALSE;
+ else
+ {
+ if (script_list)
+ *script_list = &gpos->ScriptList;
+ if (feature_list)
+ *feature_list = &gpos->FeatureList;
+ return TRUE;
+ }
+ }
+}
+
+gboolean
+pango_ot_info_find_script (PangoOTInfo *info,
+ PangoOTTableType table_type,
+ PangoOTTag script_tag,
+ guint *script_index)
+{
+ TTO_ScriptList *script_list;
+ int i;
+
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), FALSE);
+
+ if (!get_tables (info, table_type, &script_list, NULL))
+ return FALSE;
+
+ for (i=0; i < script_list->ScriptCount; i++)
+ {
+ if (script_list->ScriptRecord[i].ScriptTag == script_tag)
+ {
+ if (script_index)
+ *script_index = i;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean
+pango_ot_info_find_language (PangoOTInfo *info,
+ PangoOTTableType table_type,
+ guint script_index,
+ PangoOTTag language_tag,
+ guint *language_index,
+ guint *required_feature_index)
+{
+ TTO_ScriptList *script_list;
+ TTO_Script *script;
+ int i;
+
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), FALSE);
+
+ if (!get_tables (info, table_type, &script_list, NULL))
+ return FALSE;
+
+ g_return_val_if_fail (script_index < script_list->ScriptCount, FALSE);
+
+ script = &script_list->ScriptRecord[script_index].Script;
+
+ for (i = 0; i < script->LangSysCount; i++)
+ {
+ if (script->LangSysRecord[i].LangSysTag == language_tag)
+ {
+ if (language_index)
+ *language_index = i;
+ if (required_feature_index)
+ *required_feature_index = script->LangSysRecord[i].LangSys.ReqFeatureIndex;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+gboolean
+pango_ot_info_find_feature (PangoOTInfo *info,
+ PangoOTTableType table_type,
+ PangoOTTag feature_tag,
+ guint script_index,
+ guint language_index,
+ guint *feature_index)
+{
+ TTO_ScriptList *script_list;
+ TTO_FeatureList *feature_list;
+ TTO_Script *script;
+ TTO_LangSys *lang_sys;
+
+ int i;
+
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), FALSE);
+
+ if (!get_tables (info, table_type, &script_list, &feature_list))
+ return FALSE;
+
+ g_return_val_if_fail (script_index < script_list->ScriptCount, FALSE);
+
+ script = &script_list->ScriptRecord[script_index].Script;
+
+ if (language_index == 0xffff)
+ lang_sys = &script->DefaultLangSys;
+ else
+ {
+ g_return_val_if_fail (language_index < script->LangSysCount, FALSE);
+ lang_sys = &script->LangSysRecord[language_index].LangSys;
+ }
+
+ for (i = 0; i < lang_sys->FeatureCount; i++)
+ {
+ FT_UShort index = lang_sys->FeatureIndex[i];
+
+ if (feature_list->FeatureRecord[index].FeatureTag == feature_tag)
+ {
+ if (feature_index)
+ *feature_index = index;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+PangoOTTag *
+pango_ot_info_list_scripts (PangoOTInfo *info,
+ PangoOTTableType table_type)
+{
+ PangoOTTag *result;
+ TTO_ScriptList *script_list;
+ int i;
+
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
+
+ if (!get_tables (info, table_type, &script_list, NULL))
+ return NULL;
+
+ result = g_new (PangoOTTag, script_list->ScriptCount + 1);
+
+ for (i=0; i < script_list->ScriptCount; i++)
+ result[i] = script_list->ScriptRecord[i].ScriptTag;
+
+ result[i] = 0;
+
+ return result;
+}
+
+PangoOTTag *
+pango_ot_info_list_languages (PangoOTInfo *info,
+ PangoOTTableType table_type,
+ guint script_index,
+ PangoOTTag language_tag)
+{
+ PangoOTTag *result;
+ TTO_ScriptList *script_list;
+ TTO_Script *script;
+ int i;
+
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
+
+ if (!get_tables (info, table_type, &script_list, NULL))
+ return NULL;
+
+ g_return_val_if_fail (script_index < script_list->ScriptCount, NULL);
+
+ script = &script_list->ScriptRecord[script_index].Script;
+
+ result = g_new (PangoOTTag, script->LangSysCount + 1);
+
+ for (i = 0; i < script->LangSysCount; i++)
+ result[i] = script->LangSysRecord[i].LangSysTag;
+
+ result[i] = 0;
+
+ return result;
+}
+
+PangoOTTag *
+pango_ot_info_list_features (PangoOTInfo *info,
+ PangoOTTableType table_type,
+ PangoOTTag tag,
+ guint script_index,
+ guint language_index)
+{
+ PangoOTTag *result;
+
+ TTO_ScriptList *script_list;
+ TTO_FeatureList *feature_list;
+ TTO_Script *script;
+ TTO_LangSys *lang_sys;
+
+ int i;
+
+ g_return_val_if_fail (PANGO_IS_OT_INFO (info), NULL);
+
+ if (!get_tables (info, table_type, &script_list, &feature_list))
+ return NULL;
+
+ g_return_val_if_fail (script_index < script_list->ScriptCount, NULL);
+
+ script = &script_list->ScriptRecord[script_index].Script;
+
+ if (language_index == 0xffff)
+ lang_sys = &script->DefaultLangSys;
+ else
+ {
+ g_return_val_if_fail (language_index < script->LangSysCount, NULL);
+ lang_sys = &script->LangSysRecord[language_index].LangSys;
+ }
+
+ result = g_new (PangoOTTag, lang_sys->FeatureCount + 1);
+
+ for (i = 0; i < lang_sys->FeatureCount; i++)
+ {
+ FT_UShort index = lang_sys->FeatureIndex[i];
+
+ result[i] = feature_list->FeatureRecord[index].FeatureTag;
+ }
+
+ result[i] = 0;
+
+ return result;
+}
+
+
diff --git a/src/pango-ot-private.h b/src/pango-ot-private.h
new file mode 100644
index 00000000..782a7dde
--- /dev/null
+++ b/src/pango-ot-private.h
@@ -0,0 +1,98 @@
+/* Pango
+ * pango-ot-private.h: Implementation details for Pango OpenType code
+ *
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PANGO_OT_PRIVATE_H__
+#define __PANGO_OT_PRIVATE_H__
+
+#include <freetype/freetype.h>
+
+#include <glib-object.h>
+
+#include <pango/pango-ot.h>
+#include "ftxopen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define PANGO_TYPE_OT_INFO (pango_ot_info_get_type ())
+#define PANGO_OT_INFO(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_OT_INFO, PangoOTInfo))
+#define PANGO_OT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_OT_INFO, PangoOTInfoClass))
+#define PANGO_IS_OT_INFO(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_OT_INFO))
+#define PANGO_IS_OT_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_OT_INFO))
+#define PANGO_OT_INFO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_OT_INFO, PangoOTInfoClass))
+
+typedef struct _PangoOTInfoClass PangoOTInfoClass;
+
+struct _PangoOTInfo
+{
+ GObject parent_instance;
+
+ guint loaded;
+
+ FT_Face face;
+
+ TTO_GSUB gsub;
+ TTO_GDEF gdef;
+ TTO_GPOS gpos;
+};
+
+struct _PangoOTInfoClass
+{
+ GObjectClass parent_class;
+};
+
+#define PANGO_TYPE_OT_RULESET (pango_ot_ruleset_get_type ())
+#define PANGO_OT_RULESET(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), PANGO_TYPE_OT_RULESET, PangoOTRuleset))
+#define PANGO_OT_RULESET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PANGO_TYPE_OT_RULESET, PangoOTRulesetClass))f
+#define PANGO_OT_IS_RULESET(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), PANGO_TYPE_OT_RULESET))
+#define PANGO_OT_IS_RULESET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PANGO_TYPE_OT_RULESET))
+#define PANGO_OT_RULESET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PANGO_TYPE_OT_RULESET, PangoOTRulesetClass))
+
+typedef struct _PangoOTRulesetClass PangoOTRulesetClass;
+
+struct _PangoOTRuleset
+{
+ GObject parent_instance;
+
+ GArray *rules;
+ PangoOTInfo *info;
+};
+
+struct _PangoOTRulesetClass
+{
+ GObjectClass parent_class;
+};
+
+GType pango_ot_info_get_type (void);
+
+TTO_GDEF pango_ot_info_get_gdef (PangoOTInfo *info);
+TTO_GSUB pango_ot_info_get_gsub (PangoOTInfo *info);
+TTO_GPOS pango_ot_info_get_gpos (PangoOTInfo *info);
+
+GType pango_ot_ruleset_get_type (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __PANGO_OT_PRIVATE_H__ */
diff --git a/src/pango-ot-ruleset.c b/src/pango-ot-ruleset.c
new file mode 100644
index 00000000..79245b5a
--- /dev/null
+++ b/src/pango-ot-ruleset.c
@@ -0,0 +1,232 @@
+/* Pango
+ * pango-ot-ruleset.c: Shaping using OpenType features
+ *
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <freetype/internal/ftmemory.h>
+
+#include <pango/pango-ot.h>
+#include "pango-ot-private.h"
+
+typedef struct _PangoOTRule PangoOTRule;
+
+struct _PangoOTRule
+{
+ gulong property_bit;
+ FT_UShort feature_index;
+ guint table_type : 1;
+};
+
+static void pango_ot_ruleset_class_init (GObjectClass *object_class);
+static void pango_ot_ruleset_init (PangoOTRuleset *ruleset);
+static void pango_ot_ruleset_finalize (GObject *object);
+
+static GObjectClass *parent_class;
+
+GType
+pango_ot_ruleset_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (!object_type)
+ {
+ static const GTypeInfo object_info =
+ {
+ sizeof (PangoOTRulesetClass),
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+ (GClassInitFunc)pango_ot_ruleset_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (PangoOTRuleset),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc)pango_ot_ruleset_init,
+ };
+
+ object_type = g_type_register_static (G_TYPE_OBJECT,
+ "PangoOTRuleset",
+ &object_info, 0);
+ }
+
+ return object_type;
+}
+
+static void
+pango_ot_ruleset_class_init (GObjectClass *object_class)
+{
+ parent_class = g_type_class_peek_parent (object_class);
+
+ object_class->finalize = pango_ot_ruleset_finalize;
+}
+
+static void
+pango_ot_ruleset_init (PangoOTRuleset *ruleset)
+{
+ ruleset->rules = g_array_new (FALSE, FALSE, sizeof (PangoOTRule));
+}
+
+static void
+pango_ot_ruleset_finalize (GObject *object)
+{
+ PangoOTRuleset *ruleset = PANGO_OT_RULESET (object);
+
+ g_array_free (ruleset->rules, TRUE);
+ g_object_unref (G_OBJECT (ruleset->info));
+}
+
+PangoOTRuleset *
+pango_ot_ruleset_new (PangoOTInfo *info)
+{
+ PangoOTRuleset *ruleset;
+
+ ruleset = g_object_new (PANGO_TYPE_OT_RULESET, NULL);
+
+ ruleset->info = g_object_ref (G_OBJECT (info));
+
+ return ruleset;
+}
+
+void
+pango_ot_ruleset_add_feature (PangoOTRuleset *ruleset,
+ PangoOTTableType table_type,
+ guint feature_index,
+ gulong property_bit)
+{
+ PangoOTRule tmp_rule;
+
+ g_return_if_fail (PANGO_OT_IS_RULESET (ruleset));
+
+ tmp_rule.table_type = table_type;
+ tmp_rule.feature_index = feature_index;
+ tmp_rule.property_bit = property_bit;
+
+ g_array_append_val (ruleset->rules, tmp_rule);
+}
+
+void
+pango_ot_ruleset_shape (PangoOTRuleset *ruleset,
+ PangoGlyphString *glyphs,
+ gulong *properties)
+{
+ int i;
+ int last_cluster;
+
+ TTO_GSUB gsub = NULL;
+ TTO_GPOS gpos = NULL;
+
+ TTO_GSUB_String *in_string = NULL;
+ TTO_GSUB_String *out_string = NULL;
+ TTO_GSUB_String *result_string = NULL;
+ TTO_GPOS_Data *pos_data;
+
+ gboolean need_gsub = FALSE;
+ gboolean need_gpos = FALSE;
+
+ g_return_if_fail (PANGO_OT_IS_RULESET (ruleset));
+
+ for (i = 0; i < ruleset->rules->len; i++)
+ {
+ PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+
+ if (rule->table_type == PANGO_OT_TABLE_GSUB)
+ need_gsub = TRUE;
+ else
+ need_gpos = TRUE;
+ }
+
+ if (need_gsub)
+ {
+ gsub = pango_ot_info_get_gsub (ruleset->info);
+
+ if (gsub)
+ TT_GSUB_Clear_Features (gsub);
+ }
+
+ if (need_gpos)
+ {
+ gpos = pango_ot_info_get_gpos (ruleset->info);
+
+ if (gpos)
+ TT_GPOS_Clear_Features (gpos);
+ }
+
+ for (i = 0; i < ruleset->rules->len; i++)
+ {
+ PangoOTRule *rule = &g_array_index (ruleset->rules, PangoOTRule, i);
+
+ if (rule->table_type == PANGO_OT_TABLE_GSUB)
+ {
+ if (gsub)
+ TT_GSUB_Add_Feature (gsub, rule->feature_index, rule->property_bit);
+ }
+ else
+ {
+ if (gpos)
+ TT_GPOS_Add_Feature (gpos, rule->feature_index, rule->property_bit);
+ }
+ }
+
+ if (!gsub && !gpos)
+ return;
+
+ g_assert (TT_GSUB_String_New (ruleset->info->face->memory,
+ &in_string) == FT_Err_Ok);
+ g_assert (TT_GSUB_String_Set_Length (in_string, glyphs->num_glyphs) == FT_Err_Ok);
+
+ for (i = 0; i < glyphs->num_glyphs; i++)
+ {
+ in_string->string[i] = glyphs->glyphs[i].glyph;
+ in_string->properties[i] = properties[i];
+ in_string->logClusters[i] = glyphs->log_clusters[i];
+ }
+ in_string->max_ligID = i;
+
+ if (gsub)
+ {
+ g_assert (TT_GSUB_String_New (ruleset->info->face->memory,
+ &out_string) == FT_Err_Ok);
+ result_string = out_string;
+
+ TT_GSUB_Apply_String (gsub, in_string, out_string);
+ }
+ else
+ result_string = in_string;
+
+ pango_glyph_string_set_size (glyphs, result_string->length);
+
+ last_cluster = -1;
+ for (i = 0; i < result_string->length; i++)
+ {
+ glyphs->glyphs[i].glyph = result_string->string[i];
+ glyphs->glyphs[i].glyph = result_string->string[i];
+
+ glyphs->log_clusters[i] = result_string->logClusters[i];
+ if (glyphs->log_clusters[i] != last_cluster)
+ glyphs->glyphs[i].attr.is_cluster_start = 1;
+ else
+ glyphs->glyphs[i].attr.is_cluster_start = 0;
+
+ last_cluster = glyphs->log_clusters[i];
+ }
+
+ if (in_string)
+ TT_GSUB_String_Done (in_string);
+ if (out_string)
+ TT_GSUB_String_Done (out_string);
+}