diff options
Diffstat (limited to 'Build/source/texk/dvipdf-x/tt_gsub.c')
-rw-r--r-- | Build/source/texk/dvipdf-x/tt_gsub.c | 1666 |
1 files changed, 1666 insertions, 0 deletions
diff --git a/Build/source/texk/dvipdf-x/tt_gsub.c b/Build/source/texk/dvipdf-x/tt_gsub.c new file mode 100644 index 00000000000..ea31da94e3e --- /dev/null +++ b/Build/source/texk/dvipdf-x/tt_gsub.c @@ -0,0 +1,1666 @@ +/* + + This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. + + Copyright (C) 2002-2012 by Jin-Hwan Cho and Shunsaku Hirata, + the dvipdfmx project team. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +*/ + +/* + * TrueType GSUB support: (incomplete) + */ + +#include "system.h" + +#include "error.h" +#include "mem.h" +#include "mfileio.h" + +#include "sfnt.h" + +#include "otl_opt.h" +#include "tt_gsub.h" + +#define VERBOSE_LEVEL_MIN 0 +static int verbose = 0; +void +otl_gsub_set_verbose (void) +{ + verbose++; +} + +typedef USHORT Offset; +typedef USHORT GlyphID; + +/* OpenType Common Layout Table */ +/* Records */ +struct clt_record +{ + char tag[5]; /* 4-byte identifier */ + Offset offset; +}; + +/* Ranges */ +/* RangeRecord */ +struct clt_range +{ + GlyphID Start; /* First GlyphID in the range */ + GlyphID End; /* Last GlyphID in the range */ + USHORT StartCoverageIndex; /* Converage Index of first GID */ +}; + +static long +clt_read_record (struct clt_record *rec, sfnt *sfont) +{ + int i; + + ASSERT(rec && sfont); + + for (i = 0; i < 4; i++) { + rec->tag[i] = sfnt_get_char(sfont); + } + rec->tag[4] = '\0'; + rec->offset = sfnt_get_ushort(sfont); + + return 6; +} + +static long +clt_read_range (struct clt_range *rec, sfnt *sfont) +{ + ASSERT(rec && sfont); + + rec->Start = sfnt_get_ushort(sfont); + rec->End = sfnt_get_ushort(sfont); + rec->StartCoverageIndex = sfnt_get_ushort(sfont); + + return 6; +} + +/* + List structure: + ScriptRecord (records), FeatureRecord (records), Lookup (offsets) +*/ + +struct clt_record_list +{ + USHORT count; + struct clt_record *record; +}; + +/* offset and index list, Offset is USHORT */ +struct clt_number_list +{ + USHORT count; + USHORT *value; +}; + +static long +clt_read_record_list (struct clt_record_list *list, sfnt *sfont) +{ + long len, i; + + ASSERT(list && sfont); + + list->count = sfnt_get_ushort(sfont); + len = 2; + + if (list->count == 0) + list->record = NULL; + else { + list->record = NEW(list->count, struct clt_record); + for (i = 0; i < list->count; i++) { + len += clt_read_record(&(list->record[i]), sfont); + } + } + + return len; +} + +static void +clt_release_record_list (struct clt_record_list *list) +{ + if (list) { + if (list->record) + RELEASE(list->record); + list->record = NULL; + list->count = 0; + } +} + +static long +clt_read_number_list (struct clt_number_list *list, sfnt *sfont) +{ + long i; + + ASSERT(list && sfont); + + list->count = sfnt_get_ushort(sfont); + + if (list->count == 0) + list->value = NULL; + else { + list->value = NEW(list->count, USHORT); + for (i = 0; i < list->count; i++) { + list->value[i] = sfnt_get_ushort(sfont); + } + } + + return (2 + 2 * list->count); +} + +static void +clt_release_number_list (struct clt_number_list *list) +{ + if (list) { + if (list->value) + RELEASE(list->value); + list->value = NULL; + list->count = 0; + } +} + +/* + * Tables + */ + + +/* Coverage Table: format 1 and format 2 */ +struct clt_coverage +{ + USHORT format; /* Format identifier: 1 (list), 2 (range) */ + USHORT count; /* Glyphs/Range Count */ + GlyphID *list; /* Array of GlyphIDs - in numerical order */ + struct clt_range *range; /* Array of glyph ranges + * - ordered by Start GlyphID + */ +}; + +/* GSUB - The Glyph Substitution Table */ +struct otl_gsub_header +{ + Fixed version; /* 0x00010000 */ + Offset ScriptList; /* offset */ + Offset FeatureList; /* offset */ + Offset LookupList; /* offset */ +}; + +/* Single Substitution Format 1 */ +struct otl_gsub_single1 +{ + SHORT DeltaGlyphID; /* Add to original GlyphID to get + * substitute GlyphID */ + struct clt_coverage coverage; /* Coverage table */ +}; + +/* Single Substitution Format 2 */ +struct otl_gsub_single2 +{ + USHORT GlyphCount; /* Number of GlyphIDs in the Substitute array */ + GlyphID *Substitute; /* Array of substitute GlyphIDs + * - ordered by Coverage Index */ + struct clt_coverage coverage; /* Coverage table */ +}; + +/* Alternate Subsutitution Format 1 */ +struct otl_gsub_altset +{ + USHORT GlyphCount; /* Number of GlyphIDs in the Alternate array */ + GlyphID *Alternate; /* Array of alternate GlyphIDs + * - in arbitrary order + */ +}; + +struct otl_gsub_alternate1 +{ + USHORT AlternateSetCount; + struct otl_gsub_altset *AlternateSet; + + struct clt_coverage coverage; +}; + +/* Faithfull */ +struct otl_gsub_ligtab +{ + GlyphID LigGlyph; /* GlyphID of ligature glyph */ + USHORT CompCount; + GlyphID *Component; /* CompCount - 1 elements + * First component excluded. + * Ordered in writing direction... + */ +}; + +struct otl_gsub_ligset +{ + USHORT LigatureCount; + struct otl_gsub_ligtab *Ligature; +}; + +struct otl_gsub_ligature1 +{ + USHORT LigSetCount; + struct otl_gsub_ligset *LigatureSet; + + struct clt_coverage coverage; +}; + +/* GSUB subtable (single) */ +struct otl_gsub_subtab +{ + USHORT LookupType; /* FIXME */ + + USHORT SubstFormat; + union { + struct otl_gsub_single1 *single1; + struct otl_gsub_single2 *single2; + struct otl_gsub_alternate1 *alternate1; + struct otl_gsub_ligature1 *ligature1; + } table; +}; + +/* Script Table */ +struct clt_script_table +{ + Offset DefaultLangSys; + struct clt_record_list LangSysRecord; +}; + +static long +clt_read_script_table (struct clt_script_table *tab, sfnt *sfont) +{ + long len; + + ASSERT(tab && sfont); + + tab->DefaultLangSys = sfnt_get_ushort(sfont); + len = 2; + len += clt_read_record_list(&tab->LangSysRecord, sfont); + + return len; +} + +static void +clt_release_script_table (struct clt_script_table *tab) +{ + if (tab) + clt_release_record_list(&tab->LangSysRecord); +} + +/* LangSys Table */ +struct clt_langsys_table +{ + Offset LookupOrder; /* reserved */ + USHORT ReqFeatureIndex; + struct clt_number_list FeatureIndex; /* Array of indices into the + * FeatureList in arbitary order. + */ +}; + +static long +clt_read_langsys_table (struct clt_langsys_table *tab, sfnt *sfont) +{ + long len; + + ASSERT(tab && sfont); + + tab->LookupOrder = sfnt_get_ushort(sfont); + tab->ReqFeatureIndex = sfnt_get_ushort(sfont); + len = 4; + len += clt_read_number_list(&tab->FeatureIndex, sfont); + + return len; +} + +static void +clt_release_langsys_table (struct clt_langsys_table *tab) +{ + if (tab) + clt_release_number_list(&tab->FeatureIndex); +} + + +/* Feature Table */ +struct clt_feature_table +{ + Offset FeatureParams; + struct clt_number_list LookupListIndex; /* LookupListIndex List */ +}; + +static long +clt_read_feature_table (struct clt_feature_table *tab, sfnt *sfont) +{ + long len; + + ASSERT(tab && sfont); + + tab->FeatureParams = sfnt_get_ushort(sfont); + len = 2; + len += clt_read_number_list(&tab->LookupListIndex, sfont); + + return len; +} + +static void +clt_release_feature_table (struct clt_feature_table *tab) +{ + if (tab) + clt_release_number_list(&tab->LookupListIndex); +} + +/* Lookup Table: + * Currently, only single substitution is supported. + * LookupFlag is ignored. + */ +struct clt_lookup_table +{ + USHORT LookupType; /* Different enumerations for GSUB and GPOS */ + USHORT LookupFlag; /* Lookup qualifiers */ + struct clt_number_list SubTableList; /* offset */ + /* offset is from beginning of Lookup table */ +}; + +static long +clt_read_lookup_table (struct clt_lookup_table *tab, sfnt *sfont) +{ + long len; + + ASSERT(tab && sfont); + + tab->LookupType = sfnt_get_ushort(sfont); + tab->LookupFlag = sfnt_get_ushort(sfont); + len = 4; + len += clt_read_number_list(&tab->SubTableList, sfont); + + return len; +} + +static void +clt_release_lookup_table (struct clt_lookup_table *tab) +{ + if (tab) + clt_release_number_list(&tab->SubTableList); +} + +static long +clt_read_coverage (struct clt_coverage *cov, sfnt *sfont) +{ + long len, i; + + ASSERT(cov && sfont); + + cov->format = sfnt_get_ushort(sfont); + cov->count = sfnt_get_ushort(sfont); + len = 4; + + switch (cov->format) { + case 1: /* list */ + if (cov->count == 0) + cov->list = NULL; + else { + cov->list = NEW(cov->count, USHORT); + for (i = 0; i < cov->count; i++) { + cov->list[i] = sfnt_get_ushort(sfont); + } + } + cov->range = NULL; + len += 2 * cov->count; + break; + case 2: /* range */ + if (cov->count == 0) + cov->range = NULL; + else { + cov->range = NEW(cov->count, struct clt_range); + for (i = 0; i < cov->count; i++) { + len += clt_read_range(&(cov->range[i]), sfont); + } + } + cov->list = NULL; + break; + default: + ERROR("Unknown coverage format"); + } + + return len; +} + +static void +clt_release_coverage (struct clt_coverage *cov) +{ + if (cov) { + switch (cov->format) { + case 1: /* list */ + if (cov->list) + RELEASE(cov->list); + cov->list = NULL; + break; + case 2: /* range */ + if (cov->range) + RELEASE(cov->range); + cov->range = NULL; + break; + default: + ERROR("Unknown coverage format"); + } + } + cov->count = 0; +} + +/* returns -1 if not found */ +static long +clt_lookup_coverage (struct clt_coverage *cov, USHORT gid) +{ + long i; + + ASSERT(cov); + + switch (cov->format) { + case 1: /* list */ + for (i = 0; i < cov->count; i++) { + if (cov->list[i] > gid) { + break; + } else if (cov->list[i] == gid) { + return i; /* found */ + } + } + break; + case 2: /* range */ + for (i = 0; i < cov->count; i++) { + if (gid < cov->range[i].Start) { + break; + } else if (gid <= cov->range[i].End) { /* found */ + return (cov->range[i].StartCoverageIndex + + gid - cov->range[i].Start); + } + } + break; + default: + ERROR("Unknown coverage format"); + } + + return -1; /* not found */ +} + +static long +otl_gsub_read_single (struct otl_gsub_subtab *subtab, sfnt *sfont) +{ + long len; + ULONG offset; /* not Offset which is USHORT */ + Offset cov_offset; /* subtable offset, offset to Coverage table */ + + ASSERT(subtab && sfont); + +#ifdef XETEX + offset = sfont->loc; +#else + offset = tell_position(sfont->stream); +#endif + + subtab->LookupType = OTL_GSUB_TYPE_SINGLE; + subtab->SubstFormat = sfnt_get_ushort(sfont); + len = 2; + + if (subtab->SubstFormat == 1) { + struct otl_gsub_single1 *data; + + subtab->table.single1 = data = NEW(1, struct otl_gsub_single1); + cov_offset = sfnt_get_ushort(sfont); + data->DeltaGlyphID = sfnt_get_short(sfont); + len += 4; + + sfnt_seek_set(sfont, offset + cov_offset); + len += clt_read_coverage(&data->coverage, sfont); + + } else if (subtab->SubstFormat == 2) { + struct otl_gsub_single2 *data; + USHORT count; + + subtab->table.single2 = data = NEW(1, struct otl_gsub_single2); + cov_offset = sfnt_get_ushort(sfont); + data->GlyphCount = sfnt_get_ushort(sfont); + len += 4; + + if (data->GlyphCount == 0) + data->Substitute = NULL; + else { + data->Substitute = NEW(data->GlyphCount, GlyphID); + for (count = 0; count < data->GlyphCount; count++) { + data->Substitute[count] = sfnt_get_ushort(sfont); + } + len += 2 * data->GlyphCount; + } + + sfnt_seek_set(sfont, offset + cov_offset); + len += clt_read_coverage(&data->coverage, sfont); + + } else { + ERROR("unexpected SubstFormat"); + } + /* not implemented yet */ + + return len; +} + +static long +otl_gsub_read_alternate (struct otl_gsub_subtab *subtab, sfnt *sfont) +{ + long len; + USHORT i, j; + ULONG offset; /* not Offset which is USHORT */ + Offset cov_offset; /* subtable offset, offset to Coverage table */ + struct clt_number_list altset_offsets; + struct otl_gsub_alternate1 *data; + + ASSERT(subtab && sfont); + +#ifdef XETEX + offset = sfont->loc; +#else + offset = tell_position(sfont->stream); +#endif + + subtab->LookupType = OTL_GSUB_TYPE_ALTERNATE; + subtab->SubstFormat = sfnt_get_ushort(sfont); /* Must be 1 */ + if (subtab->SubstFormat != 1) { + WARN("Unknown GSUB SubstFormat for Alternate: %u", + subtab->SubstFormat); + return -1; + } + + len = 2; + subtab->table.alternate1 = + data = NEW(1, struct otl_gsub_alternate1); + + cov_offset = sfnt_get_ushort(sfont); + len += 2; + len += clt_read_number_list(&altset_offsets, sfont); + data->AlternateSetCount = altset_offsets.count; + if (data->AlternateSetCount == 0) { + data->AlternateSet = NULL; + data->coverage.count = 0; + data->coverage.format = 0; + data->coverage.list = NULL; + return len; + } + data->AlternateSet = NEW(data->AlternateSetCount, + struct otl_gsub_altset); + for (i = 0; i < data->AlternateSetCount; i++) { + struct otl_gsub_altset *altset; + ULONG altset_offset; + + altset = &(data->AlternateSet[i]); + + altset_offset = offset + altset_offsets.value[i]; + sfnt_seek_set(sfont, altset_offset); + altset->GlyphCount = sfnt_get_ushort(sfont); + len += 2; + if (altset->GlyphCount == 0) { + altset->Alternate = NULL; + break; + } + altset->Alternate = NEW(altset->GlyphCount, GlyphID); + for (j = 0; j < altset->GlyphCount; j++) { + altset->Alternate[j] = sfnt_get_ushort(sfont); + len += 2; + } + } + clt_release_number_list(&altset_offsets); + + sfnt_seek_set(sfont, offset + cov_offset); + len += clt_read_coverage(&data->coverage, sfont); + + return len; +} + +static long +otl_gsub_read_ligature (struct otl_gsub_subtab *subtab, sfnt *sfont) +{ + long len; + USHORT i, j; + ULONG offset; /* not Offset which is USHORT */ + Offset cov_offset; /* subtable offset, offset to Coverage table */ + struct clt_number_list ligset_offsets; + struct otl_gsub_ligature1 *data; + + ASSERT(subtab && sfont); + +#ifdef XETEX + offset = sfont->loc; +#else + offset = tell_position(sfont->stream); +#endif + + subtab->LookupType = OTL_GSUB_TYPE_LIGATURE; + subtab->SubstFormat = sfnt_get_ushort(sfont); /* Must be 1 */ + if (subtab->SubstFormat != 1) { + WARN("Unknown GSUB SubstFormat for Ligature: %u", + subtab->SubstFormat); + return -1; + } + + len = 2; + subtab->table.ligature1 = + data = NEW(1, struct otl_gsub_ligature1); + + cov_offset = sfnt_get_ushort(sfont); + len += 2; + len += clt_read_number_list(&ligset_offsets, sfont); + data->LigSetCount = ligset_offsets.count; + if (data->LigSetCount == 0) { + data->LigatureSet = NULL; + data->coverage.count = 0; + data->coverage.format = 0; + data->coverage.list = NULL; + return len; + } + data->LigatureSet = NEW(data->LigSetCount, + struct otl_gsub_ligset); + for (i = 0; i < data->LigSetCount; i++) { + struct clt_number_list ligset_tab; + struct otl_gsub_ligset *ligset; + ULONG ligset_offset; + USHORT count; + + ligset = &(data->LigatureSet[i]); + + ligset_offset = offset + ligset_offsets.value[i]; + sfnt_seek_set(sfont, ligset_offset); + len += clt_read_number_list(&ligset_tab, sfont); + + ligset->LigatureCount = ligset_tab.count; + if (ligset_tab.count == 0) { + ligset->Ligature = NULL; + break; + } + ligset->Ligature = NEW(ligset_tab.count, + struct otl_gsub_ligtab); + for (j = 0; j < ligset_tab.count; j++) { + sfnt_seek_set(sfont, ligset_offset + ligset_tab.value[j]); + ligset->Ligature[j].LigGlyph = sfnt_get_ushort(sfont); + ligset->Ligature[j].CompCount = sfnt_get_ushort(sfont); + if (ligset->Ligature[j].CompCount == 0) { + ligset->Ligature[j].Component = NULL; + break; + } + ligset->Ligature[j].Component = + NEW(ligset->Ligature[j].CompCount - 1, GlyphID); + for (count = 0; + count < ligset->Ligature[j].CompCount - 1; count++) { + ligset->Ligature[j].Component[count] = sfnt_get_ushort(sfont); + } + len += 4 + count * 2; + } + clt_release_number_list(&ligset_tab); + } + clt_release_number_list(&ligset_offsets); + + sfnt_seek_set(sfont, offset + cov_offset); + len += clt_read_coverage(&data->coverage, sfont); + + return len; +} + +static void +otl_gsub_release_single (struct otl_gsub_subtab *subtab) +{ + if (subtab) { + switch((int) subtab->SubstFormat) { + case 1: + { + struct otl_gsub_single1 *data; + + data = subtab->table.single1; + if (data) { + clt_release_coverage(&data->coverage); + RELEASE(data); + } + subtab->table.single1 = NULL; + } + break; + case 2: + { + struct otl_gsub_single2 *data; + + data = subtab->table.single2; + if (data) { + if (data->Substitute) + RELEASE(data->Substitute); + clt_release_coverage(&data->coverage); + RELEASE(data); + } + subtab->table.single2 = NULL; + } + break; + default: + ERROR("Unknown format for single substitution"); + } + } +} + +static void +otl_gsub_release_ligature (struct otl_gsub_subtab *subtab) +{ + if (subtab) { + struct otl_gsub_ligature1 *data; + USHORT i, j; + + data = subtab->table.ligature1; + if (data && data->LigatureSet) { + for (i = 0; i < data->LigSetCount; i++) { + struct otl_gsub_ligset *ligset; + + ligset = &(data->LigatureSet[i]); + for (j = 0; + j < ligset->LigatureCount; j++) { + if (ligset->Ligature[j].Component) + RELEASE(ligset->Ligature[j].Component); + ligset->Ligature[j].Component = NULL; + } + RELEASE(ligset->Ligature); + ligset->Ligature = NULL; + } + RELEASE(data->LigatureSet); + } + clt_release_coverage(&data->coverage); + data->LigatureSet = NULL; + RELEASE(data); + subtab->table.ligature1 = NULL; + } +} + +static void +otl_gsub_release_alternate (struct otl_gsub_subtab *subtab) +{ + if (subtab) { + struct otl_gsub_alternate1 *data; + USHORT i; + + data = subtab->table.alternate1; + if (data && data->AlternateSet) { + for (i = 0; i < data->AlternateSetCount; i++) { + struct otl_gsub_altset *altset; + + altset = &(data->AlternateSet[i]); + if (altset->Alternate) + RELEASE(altset->Alternate); + altset->Alternate = NULL; + } + RELEASE(data->AlternateSet); + } + clt_release_coverage(&data->coverage); + data->AlternateSet = NULL; + RELEASE(data); + subtab->table.alternate1 = NULL; + } +} + +static long +otl_gsub_read_header (struct otl_gsub_header *head, sfnt *sfont) +{ + ASSERT(head && sfont); + + head->version = sfnt_get_ulong (sfont); + head->ScriptList = sfnt_get_ushort(sfont); + head->FeatureList = sfnt_get_ushort(sfont); + head->LookupList = sfnt_get_ushort(sfont); + + return 10; +} + +/* + * script -- langsys --> feature indices + * | + * +- langsys --> feature indices + * + * feature --> lookup indices + */ + +struct otl_gsub_tab +{ + char *script; + char *language; + char *feature; + + int num_subtables; + struct otl_gsub_subtab *subtables; +}; + + +static int +otl_gsub_read_feat (struct otl_gsub_tab *gsub, sfnt *sfont) +{ + long feat_idx, script_idx; + ULONG gsub_offset, offset; + struct otl_gsub_header head; + struct otl_gsub_subtab *subtab = NULL; + USHORT num_subtabs = 0; + unsigned char feat_bits[8192]; + struct clt_record_list feature_list; + struct clt_record_list script_list; + struct clt_number_list lookup_list ; + otl_opt *script, *language, *feature; + + ASSERT(gsub && sfont); + + gsub_offset = sfnt_find_table_pos(sfont, "GSUB"); + if (gsub_offset == 0) + return -1; /* not found */ + + script = otl_new_opt(); + otl_parse_optstring(script, gsub->script); + language = otl_new_opt(); + otl_parse_optstring(language, gsub->language); + feature = otl_new_opt(); + otl_parse_optstring(feature, gsub->feature); + + memset(feat_bits, 0, 8192); + + /* GSUB header */ + sfnt_seek_set(sfont, gsub_offset); + otl_gsub_read_header(&head, sfont); + + /* Script */ + offset = gsub_offset + head.ScriptList; + sfnt_seek_set(sfont, offset); + clt_read_record_list(&script_list, sfont); + +#define SET_BIT(b,p) do {\ + (b)[(p)/8] |= (1<<(7-((p) % 8)));\ +} while (0) +#define BIT_SET(b,p) (((b)[(p)/8]) & (1 << (7-((p)%8)))) + + for (script_idx = 0; + script_idx < script_list.count; script_idx++) { + if (otl_match_optrule(script, + script_list.record[script_idx].tag)) { + struct clt_script_table script_tab; + long langsys_idx; + + offset = gsub_offset + + head.ScriptList + script_list.record[script_idx].offset; + sfnt_seek_set(sfont, offset); + clt_read_script_table(&script_tab, sfont); + + if (otl_match_optrule(language, "dflt") && + script_tab.DefaultLangSys != 0) { + struct clt_langsys_table langsys_tab; + + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("otl_gsub>> OTL script-language enabled: %c%c%c%c.dflt\n", + script_list.record[script_idx].tag[0], + script_list.record[script_idx].tag[1], + script_list.record[script_idx].tag[2], + script_list.record[script_idx].tag[3]); + } + + sfnt_seek_set(sfont, offset + script_tab.DefaultLangSys); + clt_read_langsys_table(&langsys_tab, sfont); + if (otl_match_optrule(feature, "____") && /* _FIXME_ */ + langsys_tab.ReqFeatureIndex != 0xFFFF) + SET_BIT(feat_bits, langsys_tab.ReqFeatureIndex); + for (feat_idx = 0; + feat_idx < langsys_tab.FeatureIndex.count; + feat_idx++) { + SET_BIT(feat_bits, + langsys_tab.FeatureIndex.value[feat_idx]); + } + clt_release_langsys_table(&langsys_tab); + } + for (langsys_idx = 0; + langsys_idx < script_tab.LangSysRecord.count; + langsys_idx++) { + struct clt_record *langsys_rec; + + langsys_rec = &(script_tab.LangSysRecord.record[langsys_idx]); + if (otl_match_optrule(language, langsys_rec->tag)) { + struct clt_langsys_table langsys_tab; + + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("otl_gsub>> OTL script-language enabled: %c%c%c%c.%c%c%c%c\n", + script_list.record[script_idx].tag[0], + script_list.record[script_idx].tag[1], + script_list.record[script_idx].tag[2], + script_list.record[script_idx].tag[3], + langsys_rec->tag[0], langsys_rec->tag[1], + langsys_rec->tag[2], langsys_rec->tag[3]); + } + + sfnt_seek_set(sfont, offset + langsys_rec->offset); + clt_read_langsys_table(&langsys_tab, sfont); + if (otl_match_optrule(feature, "____") || /* _FIXME_ */ + langsys_tab.ReqFeatureIndex != 0xFFFF) + SET_BIT(feat_bits, langsys_tab.ReqFeatureIndex); + for (feat_idx = 0; + feat_idx < langsys_tab.FeatureIndex.count; + feat_idx++) { + SET_BIT(feat_bits, + langsys_tab.FeatureIndex.value[feat_idx]); + } + clt_release_langsys_table(&langsys_tab); + } + } + clt_release_script_table(&script_tab); + } + } + + /* Feature List */ + offset = gsub_offset + head.FeatureList; + sfnt_seek_set(sfont, offset); + clt_read_record_list(&feature_list, sfont); + + /* Lookup List */ + offset = gsub_offset + head.LookupList; + sfnt_seek_set(sfont, offset); + clt_read_number_list(&lookup_list, sfont); + + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("otl_gsub>> Reading OTL feature(s):"); + } + + for (feat_idx = 0; + feat_idx < feature_list.count; feat_idx++) { + if (BIT_SET(feat_bits, feat_idx) && + (otl_match_optrule(feature, + feature_list.record[feat_idx].tag))) { + struct clt_feature_table feature_table; + long i; + + if(verbose > VERBOSE_LEVEL_MIN) { + MESG(" %c%c%c%c", + feature_list.record[feat_idx].tag[0], + feature_list.record[feat_idx].tag[1], + feature_list.record[feat_idx].tag[2], + feature_list.record[feat_idx].tag[3]); + } + + /* Feature Table */ + offset = gsub_offset + + head.FeatureList + feature_list.record[feat_idx].offset; + + sfnt_seek_set(sfont, offset); + clt_read_feature_table(&feature_table, sfont); + if (feature_table.FeatureParams != 0) { + ERROR("unrecognized FeatureParams"); + } + + /* Lookup table */ + for (i = 0; i < feature_table.LookupListIndex.count; i++) { + struct clt_lookup_table lookup_table; + long ll_idx, st_idx, r, n_st; + + ll_idx = feature_table.LookupListIndex.value[i]; + if (ll_idx >= lookup_list.count) + ERROR("invalid Lookup index."); + + offset = gsub_offset + + head.LookupList + (lookup_list.value)[ll_idx]; + sfnt_seek_set(sfont, offset); + clt_read_lookup_table(&lookup_table, sfont); + + if (lookup_table.LookupType != OTL_GSUB_TYPE_SINGLE && + lookup_table.LookupType != OTL_GSUB_TYPE_ALTERNATE && + lookup_table.LookupType != OTL_GSUB_TYPE_LIGATURE && + lookup_table.LookupType != OTL_GSUB_TYPE_ESUBST) { + if (verbose > VERBOSE_LEVEL_MIN) + WARN("Skipping unsupported GSUB subtable: LookupType=%d", lookup_table.LookupType); + continue; + } + + subtab = RENEW(subtab, + num_subtabs + lookup_table.SubTableList.count, + struct otl_gsub_subtab); + for (n_st = 0, st_idx = 0; + st_idx < lookup_table.SubTableList.count; st_idx++) { + + offset = gsub_offset + head.LookupList + + lookup_list.value[ll_idx] + + (lookup_table.SubTableList.value)[st_idx]; + + sfnt_seek_set(sfont, offset); + + switch ((int) lookup_table.LookupType) { + case OTL_GSUB_TYPE_SINGLE: + r = otl_gsub_read_single(&subtab[num_subtabs + n_st], + sfont); + if (r <= 0) + WARN("Reading GSUB subtable (single) failed..."); + else { + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("(single)"); + } + n_st++; + } + break; + + case OTL_GSUB_TYPE_ALTERNATE: + r = otl_gsub_read_alternate(&subtab[num_subtabs + n_st], + sfont); + if (r <= 0) + WARN("Reading GSUB subtable (alternate) failed..."); + else { + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("(alternate)"); + } + n_st++; + } + break; + + case OTL_GSUB_TYPE_LIGATURE: + r = otl_gsub_read_ligature(&subtab[num_subtabs + n_st], + sfont); + if (r <= 0) + WARN("Reading GSUB subtable (ligature) failed..."); + else { + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("(ligature)"); + } + n_st++; + } + break; + + case OTL_GSUB_TYPE_ESUBST: + { + USHORT SubstFormat; + USHORT ExtensionLookupType; + ULONG ExtensionOffset; + + SubstFormat = sfnt_get_ushort(sfont); + if (SubstFormat != 1) + break; + ExtensionLookupType = sfnt_get_ushort(sfont); + ExtensionOffset = sfnt_get_ulong (sfont); + + sfnt_seek_set(sfont, offset + ExtensionOffset); + switch (ExtensionLookupType) { + case OTL_GSUB_TYPE_SINGLE: + r = otl_gsub_read_single(&subtab[num_subtabs + n_st], + sfont); + if (r <= 0) + WARN("Reading GSUB subtable (ext:single) failed..."); + else { + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("(ext:single)"); + } + n_st++; + } + break; + + case OTL_GSUB_TYPE_ALTERNATE: + r = otl_gsub_read_alternate(&subtab[num_subtabs + n_st], + sfont); + if (r <= 0) + WARN("Reading GSUB subtable (alternate) failed..."); + else { + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("(alternate)"); + } + n_st++; + } + break; + + case OTL_GSUB_TYPE_LIGATURE: + r = otl_gsub_read_ligature(&subtab[num_subtabs + n_st], + sfont); + if (r <= 0) + WARN("Reading GSUB subtable (ext:ligature) failed..."); + else { + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("(ext:ligature)"); + } + n_st++; + } + break; + + } + } + break; + + default: + break; + } + } + num_subtabs += n_st; /* lookup_table.SubTableList.count; */ + clt_release_lookup_table(&lookup_table); + } + clt_release_feature_table(&feature_table); + } + } + + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("\n"); + MESG("otl_gsub>> %ld subtable(s) read.\n", num_subtabs); + } + + clt_release_number_list(&lookup_list); + clt_release_record_list(&feature_list); + clt_release_record_list(&script_list); + + otl_release_opt(script); + otl_release_opt(language); + otl_release_opt(feature); + + if (subtab != NULL) { + gsub->num_subtables = num_subtabs; + gsub->subtables = subtab; + } else { + return -1; + } + + return 0; +} + + +static int +otl_gsub_apply_single (struct otl_gsub_subtab *subtab, USHORT *gid) +{ + long idx; + + ASSERT(subtab && gid); + + if (subtab->SubstFormat == 1) { + struct otl_gsub_single1 *data; + + data = (subtab->table).single1; + idx = clt_lookup_coverage(&data->coverage, *gid); + if (idx >= 0) { + *gid += data->DeltaGlyphID; + return 0; /* found */ + } + } else if (subtab->SubstFormat == 2) { + struct otl_gsub_single2 *data; + + data = (subtab->table).single2; + idx = clt_lookup_coverage(&data->coverage, *gid); + if (idx >= 0 && + idx < data->GlyphCount) { + *gid = (data->Substitute)[idx]; + return 0; /* found */ + } + } + + return -1; +} + +static int +otl_gsub_apply_alternate (struct otl_gsub_subtab *subtab, + USHORT alt_idx, USHORT *gid) +{ + long idx; + + ASSERT(subtab && gid); + + if (subtab->SubstFormat == 1) { + struct otl_gsub_alternate1 *data; + + data = subtab->table.alternate1; + idx = clt_lookup_coverage(&data->coverage, *gid); + if (idx < 0 || idx >= data->AlternateSetCount) + return -1; + else { + struct otl_gsub_altset *altset; + altset = &(data->AlternateSet[idx]); + if (alt_idx >= altset->GlyphCount) + return -1; + else { + *gid = altset->Alternate[alt_idx]; + return 0; + } + } + } + + return -1; +} + +/* NOTE: Ligature table is in preference order */ +static int +glyph_seq_cmp (GlyphID *glyph_seq0, USHORT n_glyphs0, + GlyphID *glyph_seq1, USHORT n_glyphs1) +{ + USHORT i; + + if (n_glyphs0 != n_glyphs1) + return n_glyphs0 - n_glyphs1; + + for (i = 0; i < n_glyphs0; i++) { + if (glyph_seq0[i] != glyph_seq1[i]) + return glyph_seq0[i] - glyph_seq1[i]; + } + + return 0; +} + +static int +otl_gsub_apply_ligature (struct otl_gsub_subtab *subtab, + USHORT *gid_in, USHORT num_gids, + USHORT *gid_out) +{ + long idx; + + ASSERT(subtab && gid_out); + + if (!gid_in || num_gids < 1) + return -1; + + if (subtab->SubstFormat == 1) { + struct otl_gsub_ligature1 *data; + + data = subtab->table.ligature1; + idx = clt_lookup_coverage(&data->coverage, gid_in[0]); + if (idx >= 0 && idx < data->LigSetCount) { + struct otl_gsub_ligset *ligset; + USHORT j; + + ligset = &(data->LigatureSet[idx]); + for (j = 0; j < ligset->LigatureCount; j++) { + if (!glyph_seq_cmp(&gid_in[1], (USHORT)(num_gids - 1), + ligset->Ligature[j].Component, + (USHORT)(ligset->Ligature[j].CompCount - 1))) { + *gid_out = ligset->Ligature[j].LigGlyph; + return 0; /* found */ + } + } + } + } + + return -1; +} + +#define GSUB_LIST_MAX 32 +struct otl_gsub +{ + int num_gsubs; + int select; + struct otl_gsub_tab gsubs[GSUB_LIST_MAX]; +}; + +otl_gsub * +otl_gsub_new (void) +{ + struct otl_gsub *gsub_list; + + gsub_list = NEW(1, struct otl_gsub); + gsub_list->num_gsubs = 0; + gsub_list->select = -1; + + return (otl_gsub *) gsub_list; +} + +int +otl_gsub_add_feat (otl_gsub *gsub_list, + const char *script, + const char *language, + const char *feature, + sfnt *sfont) +{ + int retval = -1; + int i; + struct otl_gsub_tab *gsub; + + if (gsub_list->num_gsubs > GSUB_LIST_MAX) { + ERROR("Too many GSUB features..."); + return -1; + } + for (i = 0; i < gsub_list->num_gsubs; i++) { + gsub = &(gsub_list->gsubs[i]); + if (!strcmp(script, gsub->script) && + !strcmp(language, gsub->language) && + !strcmp(feature, gsub->feature)) { + gsub_list->select = i; + return 0; + } + } + gsub = &gsub_list->gsubs[i]; + + gsub->script = NEW(strlen(script) +1, char); + strcpy(gsub->script, script); + gsub->language = NEW(strlen(language)+1, char); + strcpy(gsub->language, language); + gsub->feature = NEW(strlen(feature) +1, char); + strcpy(gsub->feature, feature); + + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("\n"); + MESG("otl_gsub>> Reading \"%s.%s.%s\"...\n", script, language, feature); + } + + retval = otl_gsub_read_feat(gsub, sfont); + if (retval >= 0) { + gsub_list->select = i; + gsub_list->num_gsubs++; + } else { + if(verbose > VERBOSE_LEVEL_MIN) { + MESG("otl_gsub>> Failed\n"); + } + RELEASE(gsub->script); + RELEASE(gsub->language); + RELEASE(gsub->feature); + } + + return retval; +} + +void +otl_gsub_release (otl_gsub *gsub_list) +{ + struct otl_gsub_tab *gsub; + struct otl_gsub_subtab *subtab; + int i, j; + + if (!gsub_list) + return; + + for (i = 0; i < gsub_list->num_gsubs; i++) { + gsub = &(gsub_list->gsubs[i]); + + if (gsub->script) + RELEASE(gsub->script); + if (gsub->language) + RELEASE(gsub->language); + if (gsub->feature) + RELEASE(gsub->feature); + + for (j = 0; j < gsub->num_subtables; j++) { + subtab = &(gsub->subtables[j]); + switch ((int) subtab->LookupType) { + case OTL_GSUB_TYPE_SINGLE: + otl_gsub_release_single(subtab); + break; + case OTL_GSUB_TYPE_ALTERNATE: + otl_gsub_release_alternate(subtab); + break; + case OTL_GSUB_TYPE_LIGATURE: + otl_gsub_release_ligature(subtab); + break; + default: + ERROR("???"); + break; + } + } + RELEASE(gsub->subtables); + } + + RELEASE(gsub_list); +} + +int +otl_gsub_apply (otl_gsub *gsub_list, USHORT *gid) +{ + int retval = -1; + struct otl_gsub_tab *gsub; + struct otl_gsub_subtab *subtab; + int i, j; + + if (!gsub_list || !gid) + return retval; + + i = gsub_list->select; + if (i < 0 || i >= gsub_list->num_gsubs) { + ERROR("GSUB not selected..."); + return -1; + } + gsub = &(gsub_list->gsubs[i]); + + for (j = 0; + retval < 0 && j < gsub->num_subtables; j++) { + subtab = &(gsub->subtables[j]); + switch ((int) subtab->LookupType){ + case OTL_GSUB_TYPE_SINGLE: + retval = otl_gsub_apply_single(subtab, gid); + break; + default: + break; + } + } + + return retval; +} + +int +otl_gsub_apply_alt (otl_gsub *gsub_list, USHORT alt_idx, USHORT *gid) +{ + int retval = -1; + struct otl_gsub_tab *gsub; + struct otl_gsub_subtab *subtab; + int i, j; + + if (!gsub_list || !gid) + return retval; + + i = gsub_list->select; + if (i < 0 || i >= gsub_list->num_gsubs) { + ERROR("GSUB not selected..."); + return -1; + } + gsub = &(gsub_list->gsubs[i]); + + for (j = 0; + retval < 0 && j < gsub->num_subtables; j++) { + subtab = &(gsub->subtables[j]); + switch ((int) subtab->LookupType){ + case OTL_GSUB_TYPE_ALTERNATE: + retval = otl_gsub_apply_alternate(subtab, alt_idx, gid); + break; + default: + break; + } + } + + return retval; +} + +int +otl_gsub_apply_lig (otl_gsub *gsub_list, + USHORT *gid_in, USHORT num_gids, USHORT *gid_out) +{ + int retval = -1; + struct otl_gsub_tab *gsub; + struct otl_gsub_subtab *subtab; + int i, j; + + if (!gsub_list || !gid_out) + return retval; + + i = gsub_list->select; + if (i < 0 || i >= gsub_list->num_gsubs) { + ERROR("GSUB not selected..."); + return -1; + } + gsub = &(gsub_list->gsubs[i]); + + for (j = 0; + retval < 0 && j < gsub->num_subtables; j++) { + subtab = &(gsub->subtables[j]); + switch ((int) subtab->LookupType){ + case OTL_GSUB_TYPE_LIGATURE: + retval = otl_gsub_apply_ligature(subtab, + gid_in, num_gids, gid_out); + break; + default: + break; + } + } + + return retval; +} + +int +otl_gsub_select (otl_gsub *gsub_list, + const char *script, + const char *language, + const char *feature) +{ + int i; + struct otl_gsub_tab *gsub; + + for (i = 0; i < gsub_list->num_gsubs; i++) { + gsub = &(gsub_list->gsubs[i]); + if (!strcmp(gsub->script, script) && + !strcmp(gsub->language, language) && + !strcmp(gsub->feature, feature)) { + gsub_list->select = i; + return i; + } + } + + gsub_list->select = -1; + + return -1; +} + +#if 0 +static int +otl_gsub_dump_single (struct otl_gsub_subtab *subtab) +{ + long gid, idx; + + ASSERT(subtab); + + if (subtab->SubstFormat == 1) { + struct otl_gsub_single1 *data; + + data = (subtab->table).single1; + for (gid = 0; gid < 0x10000; gid++) { + idx = clt_lookup_coverage(&data->coverage, gid); + if (idx >= 0) { + fprintf(stdout, "substitute \\%u by \\%u;\n", + (USHORT) gid, (USHORT) (gid + data->DeltaGlyphID)); + } + } + } else if (subtab->SubstFormat == 2) { + struct otl_gsub_single2 *data; + + data = (subtab->table).single2; + for (gid = 0; gid < 0x10000; gid++) { + idx = clt_lookup_coverage(&data->coverage, gid); + if (idx >= 0 && + idx < data->GlyphCount) { + fprintf(stdout, "substitute \\%u by \\%u;\n", + (USHORT) gid, (data->Substitute)[idx]); + } + } + } + + return 0; +} + +static int +otl_gsub_dump_alternate (struct otl_gsub_subtab *subtab) +{ + long gid, idx; + + ASSERT(subtab); + + if (subtab->SubstFormat == 1) { + struct otl_gsub_alternate1 *data; + + data = subtab->table.alternate1; + for (gid = 0; gid < 0x10000; gid++) { + idx = clt_lookup_coverage(&data->coverage, gid); + if (idx >= 0 && idx < data->AlternateSetCount) { + struct otl_gsub_altset *altset; + USHORT i; + altset = &(data->AlternateSet[idx]); + if (altset->GlyphCount == 0) + continue; + fprintf(stdout, "substitute \\%u from [", (USHORT) gid); + for (i = 0; i < altset->GlyphCount; i++) { + fprintf(stdout, " \\%u", altset->Alternate[i]); + } + fprintf(stdout, " ];\n"); + } + } + } + + return 0; +} + +static int +otl_gsub_dump_ligature (struct otl_gsub_subtab *subtab) +{ + long gid, idx; + + ASSERT(subtab); + + if (subtab->SubstFormat == 1) { + struct otl_gsub_ligature1 *data; + + data = subtab->table.ligature1; + for (gid = 0; gid < 0x10000; gid++) { + idx = clt_lookup_coverage(&data->coverage, gid); + if (idx >= 0 && idx < data->LigSetCount) { + struct otl_gsub_ligset *ligset; + USHORT i, j; + ligset = &(data->LigatureSet[idx]); + for (j = 0; j < ligset->LigatureCount; j++) { + fprintf(stdout, "substitute \\%u", (USHORT) gid); + for (i = 0; i < ligset->Ligature[j].CompCount - 1; i++) { + fprintf(stdout, " \\%u", ligset->Ligature[j].Component[i]); + } + fprintf(stdout, " by \\%u;\n", ligset->Ligature[j].LigGlyph); + } + } + } + } + + return 0; +} + +int +otl_gsub_dump (otl_gsub *gsub_list, + const char *script, const char *language, const char *feature) +{ + int error = -1; + struct otl_gsub_tab *gsub; + struct otl_gsub_subtab *subtab; + int sel, i, j; + + if (!gsub_list) + return -1; + + sel = gsub_list->select; + error = otl_gsub_select(gsub_list, script, language, feature); + if (error < 0) { + ERROR("GSUB feature %s.%s.%s not found.", script, language, feature); + } + + i = gsub_list->select; + if (i < 0 || i >= gsub_list->num_gsubs) { + ERROR("GSUB not selected..."); + return -1; + } + gsub = &(gsub_list->gsubs[i]); + + for (j = 0; + !error && + j < gsub->num_subtables; j++) { + subtab = &(gsub->subtables[j]); + switch ((int) subtab->LookupType){ + case OTL_GSUB_TYPE_SINGLE: + error = otl_gsub_dump_single(subtab); + break; + case OTL_GSUB_TYPE_ALTERNATE: + error = otl_gsub_dump_alternate(subtab); + break; + case OTL_GSUB_TYPE_LIGATURE: + error = otl_gsub_dump_ligature(subtab); + break; + } + } + gsub_list->select = sel; + + return error; +} +#endif |