/fontforge/lookups.c
C | 6035 lines | 5333 code | 377 blank | 325 comment | 1970 complexity | 3b2070083e03feecf1d4562cdfff3c02 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.0
Large files files are truncated, but you can click here to view the full file
- #include <config.h> /* -*- coding: utf-8 -*- */
- // This file is part of the Sorts Mill Tools.
- //
- // Sorts Mill Tools 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 3 of the License, or
- // (at your option) any later version.
- //
- // Sorts Mill Tools 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, see <http://www.gnu.org/licenses/>.
- /* Copyright (C) 2007-2012 by George Williams */
- /*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
- * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
- * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
- * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- #include "fontforgevw.h"
- #include <chardata.h>
- #include <utype.h>
- #include <ustring.h>
- #include <math.h>
- #include <locale.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include "ttf.h"
- #include "lookups.h"
- VISIBLE struct opentype_feature_friendlynames friendlies[] = {
- #if 0 /* They get stuffed into the 'MATH' table now */
- /* I added these first three features to allow round-trip conversion of tfm files */
- {CHR ('I', 'T', 'L', 'C'), "ITLC", N_("Italic Correction"), gpos_single_mask},
- {CHR ('T', 'C', 'H', 'L'), "TCHL", N_("TeX Glyphlist"), gsub_alternate_mask},
- {CHR ('T', 'E', 'X', 'L'), "TEXL", N_("TeX Extension List"),
- gsub_multiple_mask},
- #endif
- /* Normal OpenType features follow */
- {CHR ('a', 'a', 'l', 't'), "aalt", N_("Access All Alternates"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('a', 'b', 'v', 'f'), "abvf", N_("Above Base Forms"), gsub_single_mask},
- {CHR ('a', 'b', 'v', 'm'), "abvm", N_("Above Base Mark"),
- gpos_mark2base_mask | gpos_mark2ligature_mask},
- {CHR ('a', 'b', 'v', 's'), "abvs", N_("Above Base Substitutions"),
- gsub_ligature_mask},
- {CHR ('a', 'f', 'r', 'c'), "afrc", N_("Vertical Fractions"),
- gsub_ligature_mask},
- {CHR ('a', 'k', 'h', 'n'), "akhn", N_("Akhand"), gsub_ligature_mask},
- {CHR ('a', 'l', 'i', 'g'), "alig", N_("Ancient Ligatures"),
- gsub_ligature_mask},
- {CHR ('b', 'l', 'w', 'f'), "blwf", N_("Below Base Forms"),
- gsub_ligature_mask},
- {CHR ('b', 'l', 'w', 'm'), "blwm", N_("Below Base Mark"),
- gpos_mark2base_mask | gpos_mark2ligature_mask},
- {CHR ('b', 'l', 'w', 's'), "blws", N_("Below Base Substitutions"),
- gsub_ligature_mask},
- {CHR ('c', '2', 'p', 'c'), "c2pc", N_("Capitals to Petite Capitals"),
- gsub_single_mask},
- {CHR ('c', '2', 's', 'c'), "c2sc", N_("Capitals to Small Capitals"),
- gsub_single_mask},
- {CHR ('c', 'a', 'l', 't'), "calt", N_("Contextual Alternates"),
- gsub_context_mask | gsub_contextchain_mask},
- {CHR ('c', 'a', 's', 'e'), "case", N_("Case-Sensitive Forms"),
- gsub_single_mask | gpos_single_mask},
- {CHR ('c', 'c', 'm', 'p'), "ccmp", N_("Glyph Composition/Decomposition"),
- gsub_multiple_mask | gsub_ligature_mask},
- {CHR ('c', 'f', 'a', 'r'), "cfar", N_("Conjunct Form After Ro"),
- gsub_single_mask},
- {CHR ('c', 'j', 'c', 't'), "cjct", N_("Conjunct Forms"), gsub_ligature_mask},
- {CHR ('c', 'l', 'i', 'g'), "clig", N_("Contextual Ligatures"),
- gsub_reversecchain_mask},
- {CHR ('c', 'p', 'c', 't'), "cpct", N_("Centered CJK Punctuation"),
- gpos_single_mask},
- {CHR ('c', 'p', 's', 'p'), "cpsp", N_("Capital Spacing"), gpos_single_mask},
- {CHR ('c', 's', 'w', 'h'), "cswh", N_("Contextual Swash"),
- gsub_reversecchain_mask},
- {CHR ('c', 'u', 'r', 's'), "curs", N_("Cursive Attachment"),
- gpos_cursive_mask},
- {CHR ('c', 'v', '0', '1'), "cv01", N_("Character Variants 01"),
- gsub_single_mask},
- {CHR ('c', 'v', '0', '2'), "cv02", N_("Character Variants 02"),
- gsub_single_mask},
- {CHR ('c', 'v', '0', '3'), "cv03", N_("Character Variants 03"),
- gsub_single_mask},
- {CHR ('c', 'v', '0', '4'), "cv04", N_("Character Variants 04"),
- gsub_single_mask},
- {CHR ('c', 'v', '0', '5'), "cv05", N_("Character Variants 05"),
- gsub_single_mask},
- {CHR ('c', 'v', '0', '6'), "cv06", N_("Character Variants 06"),
- gsub_single_mask},
- {CHR ('c', 'v', '0', '7'), "cv07", N_("Character Variants 07"),
- gsub_single_mask},
- {CHR ('c', 'v', '0', '8'), "cv08", N_("Character Variants 08"),
- gsub_single_mask},
- {CHR ('c', 'v', '0', '9'), "cv09", N_("Character Variants 09"),
- gsub_single_mask},
- {CHR ('c', 'v', '1', '0'), "cv10", N_("Character Variants 10"),
- gsub_single_mask},
- {CHR ('c', 'v', '9', '9'), "cv99", N_("Character Variants 99"),
- gsub_single_mask},
- {CHR ('d', 'c', 'a', 'p'), "dcap", N_("Drop Caps"), gsub_single_mask},
- {CHR ('d', 'i', 's', 't'), "dist", N_("Distance"), gpos_pair_mask},
- {CHR ('d', 'l', 'i', 'g'), "dlig", N_("Discretionary Ligatures"),
- gsub_ligature_mask},
- {CHR ('d', 'n', 'o', 'm'), "dnom", N_("Denominators"), gsub_single_mask},
- {CHR ('d', 'p', 'n', 'g'), "dpng", N_("Dipthongs (Obsolete)"),
- gsub_ligature_mask},
- {CHR ('d', 't', 'l', 's'), "dtls", N_("Dotless Forms"), gsub_single_mask},
- {CHR ('e', 'x', 'p', 't'), "expt", N_("Expert Forms"), gsub_single_mask},
- {CHR ('f', 'a', 'l', 't'), "falt", N_("Final Glyph On Line"),
- gsub_alternate_mask},
- {CHR ('f', 'i', 'n', '2'), "fin2", N_("Terminal Forms #2"),
- gsub_context_mask | gsub_contextchain_mask},
- {CHR ('f', 'i', 'n', '3'), "fin3", N_("Terminal Forms #3"),
- gsub_context_mask | gsub_contextchain_mask},
- {CHR ('f', 'i', 'n', 'a'), "fina", N_("Terminal Forms"), gsub_single_mask},
- {CHR ('f', 'l', 'a', 'c'), "flac", N_("Flattened Accents over Capitals"),
- gsub_single_mask | gsub_ligature_mask},
- {CHR ('f', 'r', 'a', 'c'), "frac", N_("Diagonal Fractions"),
- gsub_single_mask | gsub_ligature_mask},
- {CHR ('f', 'w', 'i', 'd'), "fwid", N_("Full Widths"),
- gsub_single_mask | gpos_single_mask},
- {CHR ('h', 'a', 'l', 'f'), "half", N_("Half Forms"), gsub_ligature_mask},
- {CHR ('h', 'a', 'l', 'n'), "haln", N_("Halant Forms"), gsub_ligature_mask},
- {CHR ('h', 'a', 'l', 't'), "halt", N_("Alternative Half Widths"),
- gpos_single_mask},
- {CHR ('h', 'i', 's', 't'), "hist", N_("Historical Forms"), gsub_single_mask},
- {CHR ('h', 'k', 'n', 'a'), "hkna", N_("Horizontal Kana Alternatives"),
- gsub_single_mask},
- {CHR ('h', 'l', 'i', 'g'), "hlig", N_("Historic Ligatures"),
- gsub_ligature_mask},
- {CHR ('h', 'n', 'g', 'l'), "hngl", N_("Hanja to Hangul"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('h', 'o', 'j', 'o'), "hojo", N_("Hojo (JIS X 0212-1990) Kanji Forms"),
- gsub_single_mask},
- {CHR ('h', 'w', 'i', 'd'), "hwid", N_("Half Widths"),
- gsub_single_mask | gpos_single_mask},
- {CHR ('i', 'n', 'i', 't'), "init", N_("Initial Forms"), gsub_single_mask},
- {CHR ('i', 's', 'o', 'l'), "isol", N_("Isolated Forms"), gsub_single_mask},
- {CHR ('i', 't', 'a', 'l'), "ital", N_("Italics"), gsub_single_mask},
- {CHR ('j', 'a', 'l', 't'), "jalt", N_("Justification Alternatives"),
- gsub_alternate_mask},
- {CHR ('j', 'a', 'j', 'p'), "jajp", N_("Japanese Forms (Obsolete)"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('j', 'p', '0', '4'), "jp04", N_("JIS2004 Forms"), gsub_single_mask},
- {CHR ('j', 'p', '7', '8'), "jp78", N_("JIS78 Forms"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('j', 'p', '8', '3'), "jp83", N_("JIS83 Forms"), gsub_single_mask},
- {CHR ('j', 'p', '9', '0'), "jp90", N_("JIS90 Forms"), gsub_single_mask},
- {CHR ('k', 'e', 'r', 'n'), "kern", N_("Horizontal Kerning"),
- gpos_pair_mask | gpos_context_mask | gpos_contextchain_mask},
- {CHR ('l', 'f', 'b', 'd'), "lfbd", N_("Left Bounds"), gpos_single_mask},
- {CHR ('l', 'i', 'g', 'a'), "liga", N_("Standard Ligatures"),
- gsub_ligature_mask},
- {CHR ('l', 'j', 'm', 'o'), "ljmo", N_("Leading Jamo Forms"),
- gsub_ligature_mask},
- {CHR ('l', 'n', 'u', 'm'), "lnum", N_("Lining Figures"), gsub_single_mask},
- {CHR ('l', 'o', 'c', 'l'), "locl", N_("Localized Forms"), gsub_single_mask},
- {CHR ('m', 'a', 'r', 'k'), "mark", N_("Mark Positioning"),
- gpos_mark2base_mask | gpos_mark2ligature_mask},
- {CHR ('m', 'e', 'd', '2'), "med2", N_("Medial Forms 2"),
- gsub_context_mask | gsub_contextchain_mask},
- {CHR ('m', 'e', 'd', 'i'), "medi", N_("Medial Forms"), gsub_single_mask},
- {CHR ('m', 'g', 'r', 'k'), "mgrk", N_("Mathematical Greek"),
- gsub_single_mask},
- {CHR ('m', 'k', 'm', 'k'), "mkmk", N_("Mark to Mark"), gpos_mark2mark_mask},
- {CHR ('m', 's', 'e', 't'), "mset", N_("Mark Positioning via Substitution"),
- gsub_context_mask | gsub_contextchain_mask},
- {CHR ('n', 'a', 'l', 't'), "nalt", N_("Alternate Annotation Forms"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('n', 'l', 'c', 'k'), "nlck", N_("NLC Kanji Forms"), gsub_single_mask},
- {CHR ('n', 'u', 'k', 't'), "nukt", N_("Nukta Forms"), gsub_ligature_mask},
- {CHR ('n', 'u', 'm', 'r'), "numr", N_("Numerators"), gsub_single_mask},
- {CHR ('o', 'n', 'u', 'm'), "onum", N_("Oldstyle Figures"), gsub_single_mask},
- {CHR ('o', 'p', 'b', 'd'), "opbd", N_("Optical Bounds"), gpos_single_mask},
- {CHR ('o', 'r', 'd', 'n'), "ordn", N_("Ordinals"),
- gsub_ligature_mask | gsub_context_mask | gsub_contextchain_mask},
- {CHR ('o', 'r', 'n', 'm'), "ornm", N_("Ornaments"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('p', 'a', 'l', 't'), "palt", N_("Proportional Alternate Metrics"),
- gpos_single_mask},
- {CHR ('p', 'c', 'a', 'p'), "pcap", N_("Lowercase to Petite Capitals"),
- gsub_single_mask},
- {CHR ('p', 'k', 'n', 'a'), "pkna", N_("Proportional Kana"), gpos_single_mask},
- {CHR ('p', 'n', 'u', 'm'), "pnum", N_("Proportional Numbers"),
- gsub_single_mask},
- {CHR ('p', 'r', 'e', 'f'), "pref", N_("Pre Base Forms"), gsub_ligature_mask},
- {CHR ('p', 'r', 'e', 's'), "pres", N_("Pre Base Substitutions"),
- gsub_ligature_mask | gsub_context_mask | gsub_contextchain_mask},
- {CHR ('p', 's', 't', 'f'), "pstf", N_("Post Base Forms"), gsub_ligature_mask},
- {CHR ('p', 's', 't', 's'), "psts", N_("Post Base Substitutions"),
- gsub_ligature_mask},
- {CHR ('p', 'w', 'i', 'd'), "pwid", N_("Proportional Width"),
- gsub_single_mask},
- {CHR ('q', 'w', 'i', 'd'), "qwid", N_("Quarter Widths"),
- gsub_single_mask | gpos_single_mask},
- {CHR ('r', 'a', 'n', 'd'), "rand", N_("Randomize"), gsub_alternate_mask},
- {CHR ('r', 'k', 'r', 'f'), "rkrf", N_("Rakar Forms"), gsub_ligature_mask},
- {CHR ('r', 'l', 'i', 'g'), "rlig", N_("Required Ligatures"),
- gsub_ligature_mask},
- {CHR ('r', 'p', 'h', 'f'), "rphf", N_("Reph Form"), gsub_ligature_mask},
- {CHR ('r', 't', 'b', 'd'), "rtbd", N_("Right Bounds"), gpos_single_mask},
- {CHR ('r', 't', 'l', 'a'), "rtla", N_("Right to Left Alternates"),
- gsub_single_mask},
- {CHR ('r', 't', 'l', 'm'), "rtlm", N_("Right to Left mirrored forms"),
- gsub_single_mask},
- {CHR ('r', 'u', 'b', 'y'), "ruby", N_("Ruby Notational Forms"),
- gsub_single_mask},
- {CHR ('s', 'a', 'l', 't'), "salt", N_("Stylistic Alternatives"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('s', 'i', 'n', 'f'), "sinf", N_("Scientific Inferiors"),
- gsub_single_mask},
- {CHR ('s', 'm', 'c', 'p'), "smcp", N_("Lowercase to Small Capitals"),
- gsub_single_mask},
- {CHR ('s', 'm', 'p', 'l'), "smpl", N_("Simplified Forms"), gsub_single_mask},
- {CHR ('s', 's', '0', '1'), "ss01", N_("Style Set 1"), gsub_single_mask},
- {CHR ('s', 's', '0', '2'), "ss02", N_("Style Set 2"), gsub_single_mask},
- {CHR ('s', 's', '0', '3'), "ss03", N_("Style Set 3"), gsub_single_mask},
- {CHR ('s', 's', '0', '4'), "ss04", N_("Style Set 4"), gsub_single_mask},
- {CHR ('s', 's', '0', '5'), "ss05", N_("Style Set 5"), gsub_single_mask},
- {CHR ('s', 's', '0', '6'), "ss06", N_("Style Set 6"), gsub_single_mask},
- {CHR ('s', 's', '0', '7'), "ss07", N_("Style Set 7"), gsub_single_mask},
- {CHR ('s', 's', '0', '8'), "ss08", N_("Style Set 8"), gsub_single_mask},
- {CHR ('s', 's', '0', '9'), "ss09", N_("Style Set 9"), gsub_single_mask},
- {CHR ('s', 's', '1', '0'), "ss10", N_("Style Set 10"), gsub_single_mask},
- {CHR ('s', 's', '1', '1'), "ss11", N_("Style Set 11"), gsub_single_mask},
- {CHR ('s', 's', '1', '2'), "ss12", N_("Style Set 12"), gsub_single_mask},
- {CHR ('s', 's', '1', '3'), "ss13", N_("Style Set 13"), gsub_single_mask},
- {CHR ('s', 's', '1', '4'), "ss14", N_("Style Set 14"), gsub_single_mask},
- {CHR ('s', 's', '1', '5'), "ss15", N_("Style Set 15"), gsub_single_mask},
- {CHR ('s', 's', '1', '6'), "ss16", N_("Style Set 16"), gsub_single_mask},
- {CHR ('s', 's', '1', '7'), "ss17", N_("Style Set 17"), gsub_single_mask},
- {CHR ('s', 's', '1', '8'), "ss18", N_("Style Set 18"), gsub_single_mask},
- {CHR ('s', 's', '1', '9'), "ss19", N_("Style Set 19"), gsub_single_mask},
- {CHR ('s', 's', '2', '0'), "ss20", N_("Style Set 20"), gsub_single_mask},
- {CHR ('s', 's', 't', 'y'), "ssty", N_("Script Style"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('s', 'u', 'b', 's'), "subs", N_("Subscript"), gsub_single_mask},
- {CHR ('s', 'u', 'p', 's'), "sups", N_("Superscript"), gsub_single_mask},
- {CHR ('s', 'w', 's', 'h'), "swsh", N_("Swash"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('t', 'i', 't', 'l'), "titl", N_("Titling"), gsub_single_mask},
- {CHR ('t', 'j', 'm', 'o'), "tjmo", N_("Trailing Jamo Forms"),
- gsub_ligature_mask},
- {CHR ('t', 'n', 'a', 'm'), "tnam", N_("Traditional Name Forms"),
- gsub_single_mask},
- {CHR ('t', 'n', 'u', 'm'), "tnum", N_("Tabular Numbers"), gsub_single_mask},
- {CHR ('t', 'r', 'a', 'd'), "trad", N_("Traditional Forms"),
- gsub_single_mask | gsub_alternate_mask},
- {CHR ('t', 'w', 'i', 'd'), "twid", N_("Third Widths"),
- gsub_single_mask | gpos_single_mask},
- {CHR ('u', 'n', 'i', 'c'), "unic", N_("Unicase"), gsub_single_mask},
- {CHR ('v', 'a', 'l', 't'), "valt", N_("Alternate Vertical Metrics"),
- gpos_single_mask},
- {CHR ('v', 'a', 't', 'u'), "vatu", N_("Vattu Variants"), gsub_ligature_mask},
- {CHR ('v', 'e', 'r', 't'), "vert", N_("Vertical Alternates (obs)"),
- gsub_single_mask},
- {CHR ('v', 'h', 'a', 'l'), "vhal", N_("Alternate Vertical Half Metrics"),
- gpos_single_mask},
- {CHR ('v', 'j', 'm', 'o'), "vjmo", N_("Vowel Jamo Forms"),
- gsub_ligature_mask},
- {CHR ('v', 'k', 'n', 'a'), "vkna", N_("Vertical Kana Alternates"),
- gsub_single_mask},
- {CHR ('v', 'k', 'r', 'n'), "vkrn", N_("Vertical Kerning"),
- gpos_pair_mask | gpos_context_mask | gpos_contextchain_mask},
- {CHR ('v', 'p', 'a', 'l'), "vpal",
- N_("Proportional Alternate Vertical Metrics"), gpos_single_mask},
- {CHR ('v', 'r', 't', '2'), "vrt2", N_("Vertical Rotation & Alternates"),
- gsub_single_mask},
- {CHR ('z', 'e', 'r', 'o'), "zero", N_("Slashed Zero"), gsub_single_mask},
- /* This is my hack for setting the "Required feature" field of a script */
- {CHR (' ', 'R', 'Q', 'D'), " RQD", N_("Required feature"),
- gsub_single_mask | gsub_multiple_mask | gsub_alternate_mask |
- gsub_ligature_mask | gsub_context_mask | gsub_contextchain_mask |
- gsub_reversecchain_mask | gpos_single_mask | gpos_pair_mask |
- gpos_cursive_mask | gpos_mark2base_mask | gpos_mark2ligature_mask |
- gpos_mark2mark_mask | gpos_context_mask | gpos_contextchain_mask},
- OPENTYPE_FEATURE_FRIENDLYNAMES_EMPTY
- };
- static int
- uint32_cmp (const void *_ui1, const void *_ui2)
- {
- if (*(uint32_t *) _ui1 > *(uint32_t *) _ui2)
- return 1;
- if (*(uint32_t *) _ui1 < *(uint32_t *) _ui2)
- return -1;
- return 0;
- }
- static int
- lang_cmp (const void *_ui1, const void *_ui2)
- {
- /* The default language is magic, and should come first in the list even */
- /* if that is not true alphabetical order */
- if (*(uint32_t *) _ui1 == DEFAULT_LANG)
- return -1;
- if (*(uint32_t *) _ui2 == DEFAULT_LANG)
- return 1;
- if (*(uint32_t *) _ui1 > *(uint32_t *) _ui2)
- return 1;
- if (*(uint32_t *) _ui1 < *(uint32_t *) _ui2)
- return -1;
- return 0;
- }
- FeatureScriptLangList *
- FindFeatureTagInFeatureScriptList (uint32_t tag, FeatureScriptLangList *fl)
- {
- while (fl != NULL)
- {
- if (fl->featuretag == tag)
- return fl;
- fl = fl->next;
- }
- return NULL;
- }
- int
- FeatureTagInFeatureScriptList (uint32_t tag, FeatureScriptLangList *fl)
- {
- while (fl != NULL)
- {
- if (fl->featuretag == tag)
- return true;
- fl = fl->next;
- }
- return false;
- }
- int
- ScriptInFeatureScriptList (uint32_t script, FeatureScriptLangList *fl)
- {
- struct scriptlanglist *sl;
- if (fl == NULL) /* No features bound to lookup? (nested?) don't restrict by script */
- return true;
- while (fl != NULL)
- {
- for (sl = fl->scripts; sl != NULL; sl = sl->next)
- {
- if (sl->script == script)
- return true;
- }
- fl = fl->next;
- }
- return false;
- }
- int
- FeatureScriptTagInFeatureScriptList (uint32_t feature, uint32_t script,
- FeatureScriptLangList *fl)
- {
- struct scriptlanglist *sl;
- while (fl != NULL)
- {
- if (fl->featuretag == feature)
- {
- for (sl = fl->scripts; sl != NULL; sl = sl->next)
- {
- if (sl->script == script)
- return true;
- }
- }
- fl = fl->next;
- }
- return false;
- }
- int
- DefaultLangTagInOneScriptList (struct scriptlanglist *sl)
- {
- int l;
- for (l = 0; l < sl->lang_cnt; ++l)
- {
- uint32_t lang = l < MAX_LANG ? sl->langs[l] : sl->morelangs[l - MAX_LANG];
- if (lang == DEFAULT_LANG)
- return true;
- }
- return false;
- }
- struct scriptlanglist *
- DefaultLangTagInScriptList (struct scriptlanglist *sl, int DFLT_ok)
- {
- while (sl != NULL)
- {
- if (DFLT_ok || sl->script != DEFAULT_SCRIPT)
- {
- if (DefaultLangTagInOneScriptList (sl))
- return sl;
- }
- sl = sl->next;
- }
- return NULL;
- }
- uint32_t *
- SFScriptsInLookups (SplineFont *sf, int gpos)
- {
- /* Presumes that either SFFindUnusedLookups or SFFindClearUnusedLookupBits */
- /* has been called first */
- /* Since MS will sometimes ignore a script if it isn't found in both */
- /* GPOS and GSUB we want to return the same script list no matter */
- /* what the setting of gpos ... so we totally ignore that argument */
- /* and always look at both sets of lookups */
- /* Sergey Malkin from MicroSoft tells me:
- Each shaping engine in Uniscribe can decide on its requirements for
- layout tables - some of them require both GSUB and GPOS, in some cases
- any table present is enough, or it can work without any table.
- Sometimes, purpose of the check is to determine if font is supporting
- particular script - if required tables are not there font is just
- rejected by this shaping engine. Sometimes, shaping engine can not just
- reject the font because there are fonts using older shaping technologies
- we still have to support, so it uses some logic when to fallback to
- legacy layout code.
- In your case this is Hebrew, where both tables are required to use
- OpenType processing. Arabic requires both tables too, Latin requires
- GSUB to execute GPOS. But in general, if you have both tables you should
- be safe with any script to get fully featured OpenType shaping.
- In other words, if we have a Hebrew font with just GPOS features they won't work,
- and MS will not use the font at all. We must add a GSUB table. In the unlikely
- event that we had a hebrew font with only GSUB it would not work either.
- So if we want our lookups to have a chance of executing under Uniscribe we
- better make sure that both tables have the same script set.
- (Sergey says we could optimize a little: A Latin GSUB table will run without
- a GPOS, but he says the GPOS won't work without a GSUB.)
- */
- int cnt = 0, tot = 0, i;
- uint32_t *scripts = NULL;
- OTLookup *test;
- FeatureScriptLangList *fl;
- struct scriptlanglist *sl;
- /* So here always give scripts for both (see comment above) no */
- /* matter what they asked for */
- for (gpos = 0; gpos < 2; ++gpos)
- {
- for (test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test != NULL;
- test = test->next)
- {
- if (test->unused)
- continue;
- for (fl = test->features; fl != NULL; fl = fl->next)
- {
- for (sl = fl->scripts; sl != NULL; sl = sl->next)
- {
- for (i = 0; i < cnt; ++i)
- {
- if (sl->script == scripts[i])
- break;
- }
- if (i == cnt)
- {
- if (cnt >= tot)
- scripts =
- xrealloc (scripts, (tot += 10) * sizeof (uint32_t));
- scripts[cnt++] = sl->script;
- }
- }
- }
- }
- }
- if (cnt == 0)
- return NULL;
- /* We want our scripts in alphabetic order */
- qsort (scripts, cnt, sizeof (uint32_t), uint32_cmp);
- /* add a 0 entry to mark the end of the list */
- if (cnt >= tot)
- scripts = xrealloc (scripts, (tot + 1) * sizeof (uint32_t));
- scripts[cnt] = 0;
- return scripts;
- }
- uint32_t *
- SFLangsInScript (SplineFont *sf, int gpos, uint32_t script)
- {
- /* However, the language lists (I think) are distinct */
- /* But giving a value of -1 for gpos will give us the set of languages in */
- /* both tables (for this script) */
- int cnt = 0, tot = 0, i, g, l;
- uint32_t *langs = NULL;
- OTLookup *test;
- FeatureScriptLangList *fl;
- struct scriptlanglist *sl;
- for (g = 0; g < 2; ++g)
- {
- if ((gpos == 0 && g == 1) || (gpos == 1 && g == 0))
- continue;
- for (test = g ? sf->gpos_lookups : sf->gsub_lookups; test != NULL;
- test = test->next)
- {
- if (test->unused)
- continue;
- for (fl = test->features; fl != NULL; fl = fl->next)
- {
- for (sl = fl->scripts; sl != NULL; sl = sl->next)
- {
- if (sl->script == script)
- {
- for (l = 0; l < sl->lang_cnt; ++l)
- {
- int lang;
- if (l < MAX_LANG)
- lang = sl->langs[l];
- else
- lang = sl->morelangs[l - MAX_LANG];
- for (i = 0; i < cnt; ++i)
- {
- if (lang == langs[i])
- break;
- }
- if (i == cnt)
- {
- if (cnt >= tot)
- langs =
- xrealloc (langs,
- (tot += 10) * sizeof (uint32_t));
- langs[cnt++] = lang;
- }
- }
- }
- }
- }
- }
- }
- if (cnt == 0)
- {
- /* We add dummy script entries. Because Uniscribe will refuse to */
- /* process some scripts if they don't have an entry in both GPOS */
- /* an GSUB. So if a script appears in either table, force it to */
- /* appear in both. That means we can get scripts with no lookups */
- /* and hence no languages. It seems that Uniscribe doesn't like */
- /* that either. So give each such script a dummy default language */
- /* entry. This is what VOLT does */
- langs = xcalloc (2, sizeof (uint32_t));
- langs[0] = DEFAULT_LANG;
- return langs;
- }
- /* We want our languages in alphabetic order */
- qsort (langs, cnt, sizeof (uint32_t), lang_cmp);
- /* add a 0 entry to mark the end of the list */
- if (cnt >= tot)
- langs = xrealloc (langs, (tot + 1) * sizeof (uint32_t));
- langs[cnt] = 0;
- return langs;
- }
- uint32_t *
- SFFeaturesInScriptLang (SplineFont *sf, int gpos, uint32_t script,
- uint32_t lang)
- {
- int cnt = 0, tot = 0, i, l, isg;
- uint32_t *features = NULL;
- OTLookup *test;
- FeatureScriptLangList *fl;
- struct scriptlanglist *sl;
- /* gpos==0 => GSUB, gpos==1 => GPOS, gpos==-1 => both, gpos==-2 => Both & morx & kern */
- if (sf->cidmaster)
- sf = sf->cidmaster;
- for (isg = 0; isg < 2; ++isg)
- {
- if (gpos >= 0 && isg != gpos)
- continue;
- for (test = isg ? sf->gpos_lookups : sf->gsub_lookups; test != NULL;
- test = test->next)
- {
- if (test->unused)
- continue;
- for (fl = test->features; fl != NULL; fl = fl->next)
- {
- if (script == 0xffffffff)
- {
- for (i = 0; i < cnt; ++i)
- {
- if (fl->featuretag == features[i])
- break;
- }
- if (i == cnt)
- {
- if (cnt >= tot)
- features =
- xrealloc (features, (tot += 10) * sizeof (uint32_t));
- features[cnt++] = fl->featuretag;
- }
- }
- else
- for (sl = fl->scripts; sl != NULL; sl = sl->next)
- {
- if (sl->script == script)
- {
- int matched = false;
- for (l = 0; l < sl->lang_cnt; ++l)
- {
- int testlang;
- if (l < MAX_LANG)
- testlang = sl->langs[l];
- else
- testlang = sl->morelangs[l - MAX_LANG];
- if (testlang == lang)
- {
- matched = true;
- break;
- }
- }
- if (matched)
- {
- for (i = 0; i < cnt; ++i)
- {
- if (fl->featuretag == features[i])
- break;
- }
- if (i == cnt)
- {
- if (cnt >= tot)
- features =
- xrealloc (features,
- (tot += 10) * sizeof (uint32_t));
- features[cnt++] = fl->featuretag;
- }
- }
- }
- }
- }
- }
- }
- if (sf->design_size != 0 && gpos)
- {
- /* The 'size' feature is like no other. It has no lookups and so */
- /* we will never find it in the normal course of events. If the */
- /* user has specified a design size, then every script/lang combo */
- /* gets a 'size' feature which contains no lookups but feature */
- /* params */
- if (cnt >= tot)
- features = xrealloc (features, (tot += 2) * sizeof (uint32_t));
- features[cnt++] = CHR ('s', 'i', 'z', 'e');
- }
- if (cnt == 0)
- return xcalloc (1, sizeof (uint32_t));
- /* We don't care if our features are in alphabetical order here */
- /* all that matters is whether the complete list of features is */
- /* ordering here would be irrelevant */
- /* qsort(features,cnt,sizeof(uint32_t),uint32_cmp); */
- /* add a 0 entry to mark the end of the list */
- if (cnt >= tot)
- features = xrealloc (features, (tot + 1) * sizeof (uint32_t));
- features[cnt] = 0;
- return features;
- }
- OTLookup **
- SFLookupsInScriptLangFeature (SplineFont *sf, int gpos, uint32_t script,
- uint32_t lang, uint32_t feature)
- {
- int cnt = 0, tot = 0, l;
- OTLookup **lookups = NULL;
- OTLookup *test;
- FeatureScriptLangList *fl;
- struct scriptlanglist *sl;
- for (test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test != NULL;
- test = test->next)
- {
- if (test->unused)
- continue;
- for (fl = test->features; fl != NULL; fl = fl->next)
- {
- if (fl->featuretag == feature)
- {
- for (sl = fl->scripts; sl != NULL; sl = sl->next)
- {
- if (sl->script == script)
- {
- for (l = 0; l < sl->lang_cnt; ++l)
- {
- int testlang;
- if (l < MAX_LANG)
- testlang = sl->langs[l];
- else
- testlang = sl->morelangs[l - MAX_LANG];
- if (testlang == lang)
- {
- if (cnt >= tot)
- lookups =
- xrealloc (lookups,
- (tot += 10) * sizeof (OTLookup *));
- lookups[cnt++] = test;
- goto found;
- }
- }
- }
- }
- }
- }
- found:;
- }
- if (cnt == 0)
- return NULL;
- /* lookup order is irrelevant here. might as well leave it in invocation order */
- /* add a 0 entry to mark the end of the list */
- if (cnt >= tot)
- lookups = xrealloc (lookups, (tot + 1) * sizeof (OTLookup *));
- lookups[cnt] = 0;
- return lookups;
- }
- static int
- LigaturesFirstComponentGID (SplineFont *sf, char *components)
- {
- int gid, ch;
- char *pt;
- for (pt = components; *pt != '\0' && *pt != ' '; ++pt);
- ch = *pt;
- *pt = '\0';
- gid = SFFindExistingSlot (sf, -1, components);
- *pt = ch;
- return gid;
- }
- static int
- PSTValid (SplineFont *sf, PST *pst)
- {
- char *start, *pt, ch;
- int ret;
- switch (pst->type)
- {
- case pst_position:
- return true;
- case pst_pair:
- return SCWorthOutputting (SFGetChar (sf, -1, pst->u.pair.paired));
- case pst_substitution:
- case pst_alternate:
- case pst_multiple:
- case pst_ligature:
- for (start = pst->u.mult.components; *start;)
- {
- for (pt = start; *pt && *pt != ' '; ++pt);
- ch = *pt;
- *pt = '\0';
- ret = SCWorthOutputting (SFGetChar (sf, -1, start));
- *pt = ch;
- if (!ret)
- return false;
- if (ch == 0)
- start = pt;
- else
- start = pt + 1;
- }
- }
- return true;
- }
- SplineChar **
- SFGlyphsWithPSTinSubtable (SplineFont *sf, struct lookup_subtable *subtable)
- {
- uint8_t *used = xcalloc (sf->glyphcnt, sizeof (uint8_t));
- SplineChar **glyphs, *sc;
- int i, k, gid, cnt;
- KernPair *kp;
- PST *pst;
- int ispair = subtable->lookup->lookup_type == gpos_pair;
- int isliga = subtable->lookup->lookup_type == gsub_ligature;
- for (i = 0; i < sf->glyphcnt; ++i)
- if (SCWorthOutputting (sc = sf->glyphs[i]))
- {
- if (ispair)
- {
- for (k = 0; k < 2; ++k)
- {
- for (kp = k ? sc->kerns : sc->vkerns; kp != NULL; kp = kp->next)
- {
- if (!SCWorthOutputting (kp->sc))
- continue;
- if (kp->subtable == subtable)
- {
- used[i] = true;
- goto continue_;
- }
- }
- }
- }
- for (pst = sc->possub; pst != NULL; pst = pst->next)
- {
- if (pst->subtable == subtable && PSTValid (sf, pst))
- {
- if (!isliga)
- {
- used[i] = true;
- goto continue_;
- }
- else
- {
- gid =
- LigaturesFirstComponentGID (sf, pst->u.lig.components);
- pst->u.lig.lig = sc;
- if (gid != -1)
- used[gid] = true;
- /* can't continue here. ffi might be "f+f+i" and "ff+i" */
- /* and we need to mark both "f" and "ff" as used */
- }
- }
- }
- continue_:;
- }
- for (i = cnt = 0; i < sf->glyphcnt; ++i)
- if (used[i])
- ++cnt;
- if (cnt == 0)
- {
- free (used);
- return NULL;
- }
- glyphs = xmalloc ((cnt + 1) * sizeof (SplineChar *));
- for (i = cnt = 0; i < sf->glyphcnt; ++i)
- {
- if (used[i])
- glyphs[cnt++] = sf->glyphs[i];
- }
- glyphs[cnt] = NULL;
- free (used);
- return glyphs;
- }
- SplineChar **
- SFGlyphsWithLigatureinLookup (SplineFont *sf, struct lookup_subtable *subtable)
- {
- uint8_t *used = xcalloc (sf->glyphcnt, sizeof (uint8_t));
- SplineChar **glyphs, *sc;
- int i, cnt;
- PST *pst;
- for (i = 0; i < sf->glyphcnt; ++i)
- if (SCWorthOutputting (sc = sf->glyphs[i]))
- {
- for (pst = sc->possub; pst != NULL; pst = pst->next)
- {
- if (pst->subtable == subtable)
- {
- used[i] = true;
- goto continue_;
- }
- }
- continue_:;
- }
- for (i = cnt = 0; i < sf->glyphcnt; ++i)
- if (used[i])
- ++cnt;
- if (cnt == 0)
- {
- free (used);
- return NULL;
- }
- glyphs = xmalloc ((cnt + 1) * sizeof (SplineChar *));
- for (i = cnt = 0; i < sf->glyphcnt; ++i)
- {
- if (used[i])
- glyphs[cnt++] = sf->glyphs[i];
- }
- glyphs[cnt] = NULL;
- free (used);
- return glyphs;
- }
- static void
- TickLookupKids (OTLookup *otl)
- {
- struct lookup_subtable *sub;
- int i, j;
- for (sub = otl->subtables; sub != NULL; sub = sub->next)
- {
- if (sub->fpst != NULL)
- {
- for (i = 0; i < sub->fpst->rule_cnt; ++i)
- {
- struct fpst_rule *rule = &sub->fpst->rules[i];
- for (j = 0; j < rule->lookup_cnt; ++j)
- {
- if (rule->lookups[j].lookup != NULL)
- rule->lookups[j].lookup->in_gpos = true;
- }
- }
- }
- }
- }
- void
- SFFindUnusedLookups (SplineFont *sf)
- {
- OTLookup *test;
- struct lookup_subtable *sub;
- int gpos;
- AnchorClass *ac;
- AnchorPoint *ap;
- SplineChar *sc;
- KernPair *kp;
- PST *pst;
- int i, k, gid, isv;
- SplineFont *_sf = sf;
- Justify *jscripts;
- struct jstf_lang *jlangs;
- if (_sf->cidmaster)
- _sf = _sf->cidmaster;
- /* Some things are obvious. If a subtable consists of a kernclass or some */
- /* such, then obviously it is used. But more distributed info takes more */
- /* work. So mark anything easy as used, and anything difficult as unused */
- /* We'll work on the difficult things later */
- for (gpos = 0; gpos < 2; ++gpos)
- {
- for (test = gpos ? _sf->gpos_lookups : _sf->gsub_lookups; test != NULL;
- test = test->next)
- {
- for (sub = test->subtables; sub != NULL; sub = sub->next)
- {
- if (sub->kc != NULL || sub->fpst != NULL)
- {
- sub->unused = false;
- continue;
- }
- sub->unused = true;
- /* We'll turn the following bit back on if there turns out */
- /* to be an anchor class attached to it -- that is subtly */
- /* different than being unused -- unused will be set if all */
- /* acs are unused, this bit will be on if there are unused */
- /* classes that still refer to us. */
- sub->anchor_classes = false;
- }
- }
- }
- /* To be useful an anchor class must have both at least one base and one mark */
- /* (for cursive anchors that means at least one entry and at least one exit) */
- /* Start by assuming the worst */
- for (ac = _sf->anchor; ac != NULL; ac = ac->next)
- ac->has_mark = ac->has_base = false;
- /* Ok, for each glyph, look at all lookups (or anchor classes) it affects */
- /* and mark the appropriate parts of them as used */
- k = 0;
- do
- {
- sf = _sf->subfontcnt == 0 ? _sf : _sf->subfonts[k];
- for (gid = 0; gid < sf->glyphcnt; ++gid)
- if (SCWorthOutputting (sc = sf->glyphs[gid]))
- {
- for (ap = sc->anchor; ap != NULL; ap = ap->next)
- {
- switch (ap->type)
- {
- case at_mark:
- case at_centry:
- ap->anchor->has_mark = true;
- break;
- case at_basechar:
- case at_baselig:
- case at_basemark:
- case at_cexit:
- ap->anchor->has_base = true;
- break;
- }
- }
- for (isv = 0; isv < 2; ++isv)
- {
- for (kp = isv ? sc->kerns : sc->vkerns; kp != NULL;
- kp = kp->next)
- {
- if (SCWorthOutputting (kp->sc))
- kp->subtable->unused = false;
- }
- }
- for (pst = sc->possub; pst != NULL; pst = pst->next)
- {
- if (pst->subtable == NULL)
- continue;
- if (!PSTValid (sf, pst))
- continue;
- pst->subtable->unused = false;
- }
- }
- ++k;
- }
- while (k < _sf->subfontcnt);
- /* Finally for any anchor class that has both a mark and a base then it is */
- /* used, and its lookup is also used */
- /* Also, even if unused, as long as the anchor class exists we must keep */
- /* the subtable around */
- for (ac = _sf->anchor; ac != NULL; ac = ac->next)
- {
- ac->subtable->anchor_classes = true;
- if (ac->has_mark && ac->has_base)
- ac->subtable->unused = false;
- }
- /* Now for each lookup, a lookup is unused if ALL subtables are unused */
- for (gpos = 0; gpos < 2; ++gpos)
- {
- for (test = gpos ? _sf->gpos_lookups : _sf->gsub_lookups; test != NULL;
- test = test->next)
- {
- test->unused = test->empty = true;
- for (sub = test->subtables; sub != NULL; sub = sub->next)
- {
- if (!sub->unused)
- test->unused = false;
- if (!sub->unused && !sub->anchor_classes)
- {
- test->empty = false;
- break;
- }
- }
- }
- }
- /* I store JSTF max lookups in the gpos list because they have the same */
- /* format. But now I need to tease them out and learn which lookups are */
- /* used in GPOS and which in JSTF (and conceivably which get duplicated */
- /* and placed in both) */
- for (test = sf->gpos_lookups; test != NULL; test = test->next)
- {
- test->only_jstf = test->in_jstf = test->in_gpos = false;
- if (test->features != NULL)
- test->in_gpos = true;
- }
- for (jscripts = sf->justify; jscripts != NULL; jscripts = jscripts->next)
- {
- for (jlangs = jscripts->langs; jlangs != NULL; jlangs = jlangs->next)
- {
- for (i = 0; i < jlangs->cnt; ++i)
- {
- struct jstf_prio *prio = &jlangs->prios[i];
- if (prio->enableShrink != NULL)
- for (k = 0; prio->enableShrink[k] != NULL; ++k)
- prio->enableShrink[k]->in_gpos = true;
- if (prio->disableShrink != NULL)
- for (k = 0; prio->disableShrink[k] != NULL; ++k)
- prio->disableShrink[k]->in_gpos = true;
- if (prio->enableExtend != NULL)
- for (k = 0; prio->enableExtend[k] != NULL; ++k)
- prio->enableExtend[k]->in_gpos = true;
- if (prio->disableExtend != NULL)
- for (k = 0; prio->disableExtend[k] != NULL; ++k)
- prio->disableExtend[k]->in_gpos = true;
- if (prio->maxShrink != NULL)
- for (k = 0; prio->maxShrink[k] != NULL; ++k)
- prio->maxShrink[k]->in_jstf = true;
- if (prio->maxExtend != NULL)
- for (k = 0; prio->maxExtend[k] != NULL; ++k)
- prio->maxExtend[k]->in_jstf = true;
- }
- }
- }
- for (test = sf->gpos_lookups; test != NULL; test = test->next)
- {
- if (test->in_gpos
- && (test->lookup_type == gpos_context
- || test->lookup_type == gpos_contextchain))
- TickLookupKids (test);
- }
- for (test = sf->gpos_lookups; test != NULL; test = test->next)
- test->only_jstf = test->in_jstf && !test->in_gpos;
- }
- void
- SFFindClearUnusedLookupBits (SplineFont *sf)
- {
- OTLookup *test;
- int gpos;
- for (gpos = 0; gpos < 2; ++gpos)
- {
- for (test = gpos ? sf->gpos_lookups : sf->gsub_lookups; test != NULL;
- test = test->next)
- {
- test->unused = false;
- test->empty = false;
- test->def_lang_checked = false;
- }
- }
- }
- static void
- SFRemoveAnchorPointsOfAC (SplineFont *sf, AnchorClass * ac)
- {
- int gid;
- SplineChar *sc;
- AnchorPoint *ap, *prev, *next;
- for (gid = 0; gid < sf->glyphcnt; ++gid)
- if ((sc = sf->glyphs[gid]) != NULL)
- {
- for (prev = NULL, ap = sc->anchor; ap != NULL; ap = next)
- {
- next = ap->next;
- if (ap->anchor != ac)
- prev = ap;
- else
- {
- if (prev == NULL)
- sc->anchor = next;
- else
- prev->next = next;
- ap->next = NULL;
- AnchorPointsFree (ap);
- }
- }
- }
- }
- static OTLookup **
- RemoveFromList (OTLookup **list, OTLookup *dying)
- {
- int i, j;
- if (list == NULL)
- return NULL;
- for (i = 0; list[i] != NULL; ++i)
- {
- if (list[i] == dying)
- {
- for (j = i + 1;; ++j)
- {
- list[j - 1] = list[j];
- if (list[j] == NULL)
- break;
- }
- }
- }
- if (list[0] == NULL)
- {
- free (list);
- return NULL;
- }
- return list;
- }
- static void
- RemoveJSTFReferences (SplineFont *sf, OTLookup *dying)
- {
- Justify *jscript;
- struct jstf_lang *jlang;
- int i;
- for (jscript = sf->justify; jscript != NULL; jscript = jscript->next)
- {
- for (jlang = jscript->langs; jlang != NULL; jlang = jlang->next)
- {
- for (i = 0; i < jlang->cnt; ++i)
- {
- struct jstf_prio *prio = &jlang->prios[i];
- prio->enableShrink = RemoveFromList (prio->enableShrink, dying);
- prio->disableShrink = RemoveFromList (prio->disableShrink, dying);
- prio->enableExtend = RemoveFromList (prio->enableExtend, dying);
- prio->disableExtend = RemoveFromList (prio->disableExtend, dying);
- prio->maxShrink = RemoveFromList (prio->maxShrink, dying);
- prio->maxExtend = RemoveFromList (prio->maxExtend, dying);
- }
- }
- }
- }
- static void
- RemoveNestedReferences (SplineFont *sf, int isgpos, OTLookup *dying)
- {
- OTLookup *otl;
- struct lookup_subtable *sub;
- int i, j, k;
- for (otl = isgpos ? sf->gpos_lookups : sf->gsub_lookups; otl != NULL;
- otl = otl->next)
- {
- /* Reverse chaining tables do not reference lookups. The match pattern */
- /* is a (exactly one) coverage table, and each glyph in that table */
- /* as an inline replacement. There is no lookup to do the replacement */
- /* (so we ignore it here) */
- if (otl->lookup_type == gsub_context
- || otl->lookup_type == gsub_contextchain
- || otl->lookup_type == gpos_context
- || otl->lookup_type == gpos_contextchain)
- {
- for (sub = otl->subtables; sub != NULL; sub = sub->next)
- {
- FPST *fpst = sub->fpst;
- for (i = 0; i < fpst->rule_cnt; ++i)
- {
- for (j = 0; j < fpst->rules[i].lookup_cnt; ++j)
- {
- if (fpst->rules[i].lookups[j].lookup == otl)
- {
- for (k = j + 1; k < fpst->rules[i].lookup_cnt; ++k)
- fpst->rules[i].lookups[k - 1] =
- fpst->rules[i].lookups[k];
- --fpst->rules[i].lookup_cnt;
- --j;
- }
- }
- }
- }
- }
- }
- }
- void
- SFRemoveUnusedLookupSubTables (SplineFont *sf,
- int remove_incomplete_anchorclasses,
- int remove_unused_lookups)
- {
- int gpos;
- struct lookup_subtable *sub, *subnext, *prev;
- AnchorClass *ac, *acprev, *acnext;
- OTLookup *otl, *otlprev, *otlnext;
- /* Presumes someone has called SFFindUnusedLookups first */
- if (remove_incomplete_anchorclasses)
- {
- for (acprev = NULL, ac = sf->anchor; ac != NULL; ac = acnext)
- {
- acnext = ac->next;
- if (ac->has_mark && ac->has_base)
- acprev = ac;
- else
- {
- SFRemoveAnchorPointsOfAC (sf, ac);
- ac->next = NULL;
- AnchorClassesFree (ac);
- if (acprev == NULL)
- sf->anchor = acnext;
- else
- acprev = acnext;
- }
- }
- }
- for (gpos = 0; gpos < 2; ++gpos)
- {
- for (otl = gpos ? sf->gpos_lookups : sf->gsub_lookups; otl != NULL;
- otl = otlnext)
- {
- otlnext = otl->next;
- if (remove_unused_lookups && (otl->empty ||
- (otl->unused
- && remove_incomplete_anchorclasses)))
- {
- if (otlprev != NULL)
- otlprev->next = otlnext;
- else if (gpos)
- sf->gpos_lookups = otlnext;
- else
- sf->gsub_lookups = otlnext;
- RemoveNestedReferences (sf, gpos, otl);
- OTLookupFree (otl);
- }
- else
- {
- for (prev = NULL, sub = otl->subtables; sub != NULL;
- sub = subnext)
- {
- subnext = sub->next;
- if (sub->unused &&
- (!sub->anchor_classes || remove_incomplete_anchorclasses))
- {
- if (prev == NULL)
- otl->subtables = subnext;
- else
- prev->next = subnext;
- free (sub->subtable_name);
- free (sub);
- }
- else
- prev = sub;
- }
- }
- }
- }
- }
- void
- SFRemoveLookupSubTable (SplineFont *sf, struct lookup_subtable *sub)
- {
- OTLookup *otl = sub->lookup;
- struct lookup_subtable *subprev, *subtest;
- if (sf->cidmaster != NULL)
- sf = sf->cidmaster;
- if (sub->fpst != NULL)
- {
- FPST *prev = NULL, *test;
- for (test = sf->possub; test != NULL && test != sub->fpst;
- prev = test, test = test->next);
- if (prev == NULL)
- sf->possub = sub->fpst->next;
- else
- prev->next = sub->fpst->next;
- sub->fpst->next = NULL;
- FPSTFree (sub->fpst);
- sub->fpst = NULL;
- }
- else if (sub->kc != NULL)
- {
- KernClass *prev = NULL, *test;
- for (test = sf->kerns; test != NULL && test != sub->kc;
- prev = test, test = test->next);
- if (test != NULL)
- {
- if (prev == NULL)
- sf->kerns = sub->kc->next;
- else
- prev->next = sub->kc->next;
- }
- else
- {
- for (prev = NULL, test = sf->vkerns; test != NULL && test != sub->kc;
- prev = test, test = test->next);
- if (prev == NULL)
- sf->vkerns = sub->kc->next;
- else
- prev->next = sub->kc->next;
- }
- sub->kc->next = NULL;
- KernClassListFree (sub->kc);
- sub->kc = NULL;
- }
- else if (otl->lookup_type == gpos_cursive
- || otl->lookup_type == gpos_mark2base
- || otl->lookup_type == gpos_mark2ligature
- || otl->lookup_type == gpos_mark2mark)
- {
- AnchorClass *ac, *acnext;
- for (ac = sf->anchor; ac != NULL; ac = acnext)
- {
- acnext = ac->next;
- if (ac->subtable == sub)
- SFRemoveAnchorClass (sf, ac);
- }
- }
- else
- {
- int i, k, v;
- SplineChar *sc;
- SplineFont *_sf;
- PST *pst, *prev, *next;
- KernPair *kp, *kpprev, *kpnext;
- k = 0;
- do
- {
- _sf = sf->subfontcnt == 0 ? sf : sf->subfonts[i];
- for (i = 0; i < _sf->gl…
Large files files are truncated, but you can click here to view the full file