From 80a15829135065e16ce5b129c715d10d14b829ba Mon Sep 17 00:00:00 2001 From: Owen Taylor Date: Wed, 20 Dec 2000 04:41:36 +0000 Subject: [PATCH] Since Xft may only be available statically without shlib deps, check for Tue Dec 19 22:47:16 2000 Owen Taylor * 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. --- src/.cvsignore | 8 + src/FT-license.txt | 158 ++ src/Makefile.am | 39 + src/README | 36 + src/disasm.c | 317 +++ src/disasm.h | 26 + src/ftxgdef.c | 1155 +++++++++ src/ftxgdef.h | 220 ++ src/ftxgpos.c | 6222 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ftxgpos.h | 858 +++++++ src/ftxgsub.c | 4531 +++++++++++++++++++++++++++++++++++ src/ftxgsub.h | 612 +++++ src/ftxopen.c | 1467 ++++++++++++ src/ftxopen.h | 308 +++ src/ftxopenf.h | 161 ++ src/ottest.c | 265 +++ src/pango-ot-info.c | 438 ++++ src/pango-ot-private.h | 98 + src/pango-ot-ruleset.c | 232 ++ 19 files changed, 17151 insertions(+) create mode 100644 src/.cvsignore create mode 100644 src/FT-license.txt create mode 100644 src/Makefile.am create mode 100644 src/README create mode 100644 src/disasm.c create mode 100644 src/disasm.h create mode 100644 src/ftxgdef.c create mode 100644 src/ftxgdef.h create mode 100644 src/ftxgpos.c create mode 100644 src/ftxgpos.h create mode 100644 src/ftxgsub.c create mode 100644 src/ftxgsub.h create mode 100644 src/ftxopen.c create mode 100644 src/ftxopen.h create mode 100644 src/ftxopenf.h create mode 100644 src/ottest.c create mode 100644 src/pango-ot-info.c create mode 100644 src/pango-ot-private.h create mode 100644 src/pango-ot-ruleset.c diff --git a/src/.cvsignore b/src/.cvsignore new file mode 100644 index 0000000..5195f99 --- /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 0000000..8dae609 --- /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 + Robert Wilhelm + Werner Lemberg + + +--- end of license.txt --- diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..38cbfd2 --- /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 0000000..6deb7f5 --- /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 0000000..2e064ec --- /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 + +#include "disasm.h" + +#define DUMP(args...) dump (stream, indent, args) +#define DUMP_FINT(strct,fld) dump (stream, indent, "<" #fld ">%d\n", (strct)->fld) +#define DUMP_FUINT(strct,fld) dump (stream, indent, "<" #fld ">%u\n", (strct)->fld) +#define DUMP_FGLYPH(strct,fld) dump (stream, indent, "<" #fld ">%#4x\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 ("\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("%d\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, ""); + Print_Tag (Script->LangSysRecord[i].LangSysTag, stream); + fprintf (stream, "\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, ""); + Print_Tag (ScriptList->ScriptRecord[i].ScriptTag, stream); + fprintf (stream, "\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("%d\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, ""); + Print_Tag (FeatureList->FeatureRecord[i].FeatureTag, stream); + fprintf (stream, "\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("%#4x \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("%#4x \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("%#4x\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("%s\n", lookup_name); + + for (i=0; i < Lookup->SubTableCount; i++) + { + DUMP ("\n"); + if (lookup_func) + (*lookup_func) (&Lookup->SubTable[i], stream, indent + 1, is_gsub); + DUMP ("\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 0000000..e7556d4 --- /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 +#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 0000000..dd1754c --- /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 + +#include +#include +#include +#include + +#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 0000000..6395bf3 --- /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 0000000..48d5009 --- /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 + +#include +#include +#include +#include + +#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 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 0000000..56ffb78 --- /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 0000000..43adb2c --- /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 + +#include +#include +#include +#include + +#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 0000000..3f4f8ad --- /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 0000000..5ffe030 --- /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 +#include +#include +#include + +#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 0000000..28dff90 --- /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 + +#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 0000000..bbb2c85 --- /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 0000000..c6338e9 --- /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 +#include + +#include "ftxopen.h" +#include + +#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 0000000..b426e32 --- /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 +#include +#include + +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 0000000..782a7dd --- /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 + +#include + +#include +#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 0000000..79245b5 --- /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 + +#include +#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); +} -- 2.7.4