diff --git a/src/Makefile.am b/src/Makefile.am index acedaa0b6..ba07ac5d6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -91,6 +91,7 @@ HBSOURCES += \ hb-ot-shape-complex-arabic.cc \ hb-ot-shape-complex-arabic-fallback.hh \ hb-ot-shape-complex-arabic-table.hh \ + hb-ot-shape-complex-arabic-win1256.hh \ hb-ot-shape-complex-default.cc \ hb-ot-shape-complex-hangul.cc \ hb-ot-shape-complex-hebrew.cc \ diff --git a/src/check-includes.sh b/src/check-includes.sh index 5643c6973..902f2357e 100755 --- a/src/check-includes.sh +++ b/src/check-includes.sh @@ -27,7 +27,7 @@ echo 'Checking that source files #include "hb-*private.hh" first (or none)' for x in $HBSOURCES; do test -f "$srcdir/$x" && x="$srcdir/$x" - grep '#.*\' "$x" /dev/null | head -n 1 + grep '#.*\' "$x" /dev/null | grep -v 'include _' | head -n 1 done | grep -v '"hb-.*private[.]hh"' | grep -v 'hb-private[.]hh:' | diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh index d488885dc..e62cea479 100644 --- a/src/hb-ot-shape-complex-arabic-fallback.hh +++ b/src/hb-ot-shape-complex-arabic-fallback.hh @@ -33,7 +33,8 @@ #include "hb-ot-layout-gsub-table.hh" -/* Features ordered the same as the entries in shaping_table rows. */ +/* Features ordered the same as the entries in shaping_table rows, + * followed by rlig. Don't change. */ static const hb_tag_t arabic_fallback_features[] = { HB_TAG('i','n','i','t'), @@ -43,16 +44,6 @@ static const hb_tag_t arabic_fallback_features[] = HB_TAG('r','l','i','g'), }; -/* Same order as the fallback feature array */ -enum { - FALLBACK_INIT, - FALLBACK_MEDI, - FALLBACK_FINA, - FALLBACK_ISOL, - FALLBACK_RLIG, - ARABIC_NUM_FALLBACK_FEATURES -}; - static OT::SubstLookup * arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUSED, hb_font_t *font, @@ -81,6 +72,9 @@ arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan HB_UNUS num_glyphs++; } + if (!num_glyphs) + return NULL; + /* Bubble-sort! * May not be good-enough for presidential candidate interviews, but good-enough for us... */ hb_bubble_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]); @@ -158,6 +152,9 @@ arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan HB_UN } } + if (!num_ligatures) + return NULL; + OT::Supplier first_glyphs_supplier (first_glyphs, num_first_glyphs); OT::Supplier ligature_per_first_glyph_count_supplier (ligature_per_first_glyph_count_list, num_first_glyphs); OT::Supplier ligatures_supplier (ligature_list, num_ligatures); @@ -194,17 +191,108 @@ arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan, return arabic_fallback_synthesize_lookup_ligature (plan, font); } +#define ARABIC_FALLBACK_MAX_LOOKUPS 5 + struct arabic_fallback_plan_t { ASSERT_POD (); - hb_mask_t mask_array[ARABIC_NUM_FALLBACK_FEATURES]; - OT::SubstLookup *lookup_array[ARABIC_NUM_FALLBACK_FEATURES]; - hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_NUM_FALLBACK_FEATURES]; + unsigned int num_lookups; + bool free_lookups; + + hb_mask_t mask_array[ARABIC_FALLBACK_MAX_LOOKUPS]; + OT::SubstLookup *lookup_array[ARABIC_FALLBACK_MAX_LOOKUPS]; + hb_ot_layout_lookup_accelerator_t accel_array[ARABIC_FALLBACK_MAX_LOOKUPS]; }; static const arabic_fallback_plan_t arabic_fallback_plan_nil = {}; +#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(HB_WITH_WIN1256) +#define HB_WITH_WIN1256 +#endif + +#ifdef HB_WITH_WIN1256 +#include "hb-ot-shape-complex-arabic-win1256.hh" +#endif + +struct ManifestLookup { + OT::Tag tag; + OT::OffsetTo lookupOffset; +}; +typedef OT::ArrayOf Manifest; + +static bool +arabic_fallback_plan_init_win1256 (arabic_fallback_plan_t *fallback_plan, + const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ +#ifdef HB_WITH_WIN1256 + /* Does this font look like it's Windows-1256-encoded? */ + hb_codepoint_t g; + if (!(hb_font_get_glyph (font, 0x0627u, 0, &g) && g == 199 /* ALEF */ && + hb_font_get_glyph (font, 0x0644u, 0, &g) && g == 225 /* LAM */ && + hb_font_get_glyph (font, 0x0649u, 0, &g) && g == 236 /* ALEF MAKSURA */ && + hb_font_get_glyph (font, 0x064Au, 0, &g) && g == 237 /* YEH */ && + hb_font_get_glyph (font, 0x0652u, 0, &g) && g == 250 /* SUKUN */)) + return false; + + const Manifest &manifest = reinterpret_cast (arabic_win1256_gsub_lookups.manifest); + ASSERT_STATIC (sizeof (arabic_win1256_gsub_lookups.manifestData) / sizeof (ManifestLookup) + <= ARABIC_FALLBACK_MAX_LOOKUPS); + /* TODO sanitize the table? */ + + unsigned j = 0; + unsigned int count = manifest.len; + for (unsigned int i = 0; i < count; i++) + { + fallback_plan->mask_array[j] = plan->map.get_1_mask (manifest[i].tag); + if (fallback_plan->mask_array[j]) + { + fallback_plan->lookup_array[j] = const_cast (&(&manifest+manifest[i].lookupOffset)); + if (fallback_plan->lookup_array[j]) + { + fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); + j++; + } + } + } + + fallback_plan->num_lookups = j; + fallback_plan->free_lookups = false; + + return j > 0; +#else + return false; +#endif +} + +static bool +arabic_fallback_plan_init_unicode (arabic_fallback_plan_t *fallback_plan, + const hb_ot_shape_plan_t *plan, + hb_font_t *font) +{ + ASSERT_STATIC (ARRAY_LENGTH(arabic_fallback_features) <= ARABIC_FALLBACK_MAX_LOOKUPS); + unsigned int j = 0; + for (unsigned int i = 0; i < ARRAY_LENGTH(arabic_fallback_features) ; i++) + { + fallback_plan->mask_array[j] = plan->map.get_1_mask (arabic_fallback_features[i]); + if (fallback_plan->mask_array[j]) + { + fallback_plan->lookup_array[j] = arabic_fallback_synthesize_lookup (plan, font, i); + if (fallback_plan->lookup_array[j]) + { + fallback_plan->accel_array[j].init (*fallback_plan->lookup_array[j]); + j++; + } + } + } + + fallback_plan->num_lookups = j; + fallback_plan->free_lookups = true; + + return j > 0; +} + static arabic_fallback_plan_t * arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, hb_font_t *font) @@ -213,17 +301,21 @@ arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan, if (unlikely (!fallback_plan)) return const_cast (&arabic_fallback_plan_nil); - for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) - { - fallback_plan->mask_array[i] = plan->map.get_1_mask (arabic_fallback_features[i]); - if (fallback_plan->mask_array[i]) { - fallback_plan->lookup_array[i] = arabic_fallback_synthesize_lookup (plan, font, i); - if (fallback_plan->lookup_array[i]) - fallback_plan->accel_array[i].init (*fallback_plan->lookup_array[i]); - } - } + fallback_plan->num_lookups = 0; + fallback_plan->free_lookups = false; - return fallback_plan; + /* Try synthesizing GSUB table using Unicode Arabic Presentation Forms, + * in case the font has cmap entries for the presentation-forms characters. */ + if (arabic_fallback_plan_init_unicode (fallback_plan, plan, font)) + return fallback_plan; + + /* See if this looks like a Windows-1256-encoded font. If it does, use a + * hand-coded GSUB table. */ + if (arabic_fallback_plan_init_win1256 (fallback_plan, plan, font)) + return fallback_plan; + + free (fallback_plan); + return const_cast (&arabic_fallback_plan_nil); } static void @@ -232,11 +324,12 @@ arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan) if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil) return; - for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) + for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) if (fallback_plan->lookup_array[i]) { fallback_plan->accel_array[i].fini (fallback_plan->lookup_array[i]); - free (fallback_plan->lookup_array[i]); + if (fallback_plan->free_lookups) + free (fallback_plan->lookup_array[i]); } free (fallback_plan); @@ -248,7 +341,7 @@ arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan, hb_buffer_t *buffer) { OT::hb_apply_context_t c (0, font, buffer); - for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) + for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) if (fallback_plan->lookup_array[i]) { c.set_lookup_mask (fallback_plan->mask_array[i]); hb_ot_layout_substitute_lookup (&c, diff --git a/src/hb-ot-shape-complex-arabic-win1256.hh b/src/hb-ot-shape-complex-arabic-win1256.hh new file mode 100644 index 000000000..63ed3f8e0 --- /dev/null +++ b/src/hb-ot-shape-complex-arabic-win1256.hh @@ -0,0 +1,321 @@ +/* + * Copyright © 2014 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH + + +/* + * The macros in the first part of this file are generic macros that can + * be used to define the bytes for OpenType table data in code in a + * readable manner. We can move the macros to reside with their respective + * struct types, but since we only use these to define one data table, the + * Windows-1256 Arabic shaping table in this file, we keep them here. + */ + + +/* First we measure, then we cut. */ +#ifndef OT_MEASURE +#define OT_MEASURE +#define OT_TABLE_START static const struct TABLE_NAME { +#define OT_TABLE_END } +#define OT_LABEL_START(Name) unsigned char Name[ +#define OT_LABEL_END ]; +#define OT_BYTE(u8) +1/*byte*/ +#define OT_USHORT(u16) +2/*bytes*/ +#else +#undef OT_MEASURE +#define OT_TABLE_START TABLE_NAME = { +#define OT_TABLE_END }; +#define OT_LABEL_START(Name) { +#define OT_LABEL_END }, +#define OT_BYTE(u8) (u8), +#define OT_USHORT(u16) (unsigned char)((u16)>>8), (unsigned char)((u16)&0xFFu), +#define OT_COUNT(Name, ItemSize) ((unsigned int) sizeof(((struct TABLE_NAME*)0)->Name) \ + / (unsigned int)(ItemSize) \ + /* OT_ASSERT it's divisible (and positive). */) +#define OT_DISTANCE(From,To) ((unsigned int) \ + ((char*)(&((struct TABLE_NAME*)0)->To) - \ + (char*)(&((struct TABLE_NAME*)0)->From)) \ + /* OT_ASSERT it's positive. */) +#endif + + +#define OT_LABEL(Name) \ + OT_LABEL_END \ + OT_LABEL_START(Name) + +/* Whenever we receive an argument that is a list, it will expand to + * contain commas. That cannot be passed to another macro because the + * commas will throw off the preprocessor. The solution is to wrap + * the passed-in argument in OT_LIST() before passing to the next macro. + * Unfortunately this trick requires vararg macros. */ +#define OT_LIST(...) __VA_ARGS__ + + +/* + * Basic Types + */ + +#define OT_TAG(a,b,c,d) \ + OT_BYTE(a) OT_BYTE(b) OT_BYTE(c) OT_BYTE(d) + +#define OT_OFFSET(From, To) /* Offset from From to To in bytes */ \ + OT_USHORT(OT_DISTANCE(From, To)) + +#define OT_GLYPHID /* GlyphID */ \ + OT_USHORT + +#define OT_UARRAY(Name, Items) \ + OT_LABEL_START(Name) \ + OT_USHORT(OT_COUNT(Name##Data, 2)) \ + OT_LABEL(Name##Data) \ + Items \ + OT_LABEL_END + +#define OT_UHEADLESSARRAY(Name, Items) \ + OT_LABEL_START(Name) \ + OT_USHORT(OT_COUNT(Name##Data, 2) + 1) \ + OT_LABEL(Name##Data) \ + Items \ + OT_LABEL_END + + +/* + * Common Types + */ + +#define OT_LOOKUP_FLAG_IGNORE_MARKS 0x08u + +#define OT_LOOKUP(Name, LookupType, LookupFlag, SubLookupOffsets) \ + OT_LABEL_START(Name) \ + OT_USHORT(LookupType) \ + OT_USHORT(LookupFlag) \ + OT_LABEL_END \ + OT_UARRAY(Name##SubLookupOffsetsArray, OT_LIST(SubLookupOffsets)) + +#define OT_SUBLOOKUP(Name, SubFormat, Items) \ + OT_LABEL_START(Name) \ + OT_USHORT(SubFormat) \ + Items + +#define OT_COVERAGE1(Name, Items) \ + OT_LABEL_START(Name) \ + OT_USHORT(1) \ + OT_LABEL_END \ + OT_UARRAY(Name##Glyphs, OT_LIST(Items)) + + +/* + * GSUB + */ + +#define OT_LOOKUP_TYPE_SUBST_SINGLE 1u +#define OT_LOOKUP_TYPE_SUBST_MULTIPLE 2u +#define OT_LOOKUP_TYPE_SUBST_LIGATURE 4u + +#define OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(Name, FromGlyphs, ToGlyphs) \ + OT_SUBLOOKUP(Name, 2, \ + OT_OFFSET(Name, Name##Coverage) \ + OT_LABEL_END \ + OT_UARRAY(Name##Substitute, OT_LIST(ToGlyphs)) \ + ) \ + OT_COVERAGE1(Name##Coverage, OT_LIST(FromGlyphs)) \ + /* ASSERT_STATIC_EXPR len(FromGlyphs) == len(ToGlyphs) */ + +#define OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(Name, FirstGlyphs, LigatureSetOffsets) \ + OT_SUBLOOKUP(Name, 1, \ + OT_OFFSET(Name, Name##Coverage) \ + OT_LABEL_END \ + OT_UARRAY(Name##LigatureSetOffsetsArray, OT_LIST(LigatureSetOffsets)) \ + ) \ + OT_COVERAGE1(Name##Coverage, OT_LIST(FirstGlyphs)) \ + /* ASSERT_STATIC_EXPR len(FirstGlyphs) == len(LigatureSetOffsets) */ + +#define OT_LIGATURE_SET(Name, LigatureSetOffsets) \ + OT_UARRAY(Name, OT_LIST(LigatureSetOffsets)) + +#define OT_LIGATURE(Name, Components, LigGlyph) \ + OT_LABEL_START(Name) \ + LigGlyph \ + OT_LABEL_END \ + OT_UHEADLESSARRAY(Name##ComponentsArray, OT_LIST(Components)) + +/* + * + * Start of Windows-1256 shaping table. + * + */ + +/* Table name. */ +#define TABLE_NAME arabic_win1256_gsub_lookups + +/* Table manifest. */ +#define MANIFEST(Items) \ + OT_LABEL_START(manifest) \ + OT_USHORT(OT_COUNT(manifestData, 6)) \ + OT_LABEL(manifestData) \ + Items \ + OT_LABEL_END + +#define MANIFEST_LOOKUP(Tag, Name) \ + Tag \ + OT_OFFSET(manifest, Name) + +/* Shorthand. */ +#define G OT_GLYPHID + +/* + * Table Start + */ +OT_TABLE_START + + +/* + * Manifest + */ +MANIFEST( + MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligLookup) + MANIFEST_LOOKUP(OT_TAG('i','n','i','t'), initLookup) + MANIFEST_LOOKUP(OT_TAG('m','e','d','i'), mediLookup) + MANIFEST_LOOKUP(OT_TAG('f','i','n','a'), finaLookup) + MANIFEST_LOOKUP(OT_TAG('r','l','i','g'), rligMarksLookup) +) + +/* + * Lookups + */ +OT_LOOKUP(initLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(initLookup, initmediSubLookup) + OT_OFFSET(initLookup, initSubLookup) +) +OT_LOOKUP(mediLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(mediLookup, initmediSubLookup) + OT_OFFSET(mediLookup, mediSubLookup) + OT_OFFSET(mediLookup, medifinaLamAlefSubLookup) +) +OT_LOOKUP(finaLookup, OT_LOOKUP_TYPE_SUBST_SINGLE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(finaLookup, finaSubLookup) + /* We don't need this one currently as the sequence inherits masks + * from the first item. Just in case we change that in the future + * to be smart about Arabic masks when ligating... */ + OT_OFFSET(finaLookup, medifinaLamAlefSubLookup) +) +OT_LOOKUP(rligLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, OT_LOOKUP_FLAG_IGNORE_MARKS, + OT_OFFSET(rligLookup, lamAlefLigaturesSubLookup) +) +OT_LOOKUP(rligMarksLookup, OT_LOOKUP_TYPE_SUBST_LIGATURE, 0, + OT_OFFSET(rligMarksLookup, shaddaLigaturesSubLookup) +) + +/* + * init/medi/fina forms + */ +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initmediSubLookup, + G(198) G(200) G(201) G(202) G(203) G(204) G(205) G(206) G(211) + G(212) G(213) G(214) G(223) G(225) G(227) G(228) G(236) G(237), + G(162) G(4) G(5) G(5) G(6) G(7) G(9) G(11) G(13) + G(14) G(15) G(26) G(140) G(141) G(142) G(143) G(154) G(154) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(initSubLookup, + G(218) G(219) G(221) G(222) G(229), + G(27) G(30) G(128) G(131) G(144) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(mediSubLookup, + G(218) G(219) G(221) G(222) G(229), + G(28) G(31) G(129) G(138) G(149) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(finaSubLookup, + G(194) G(195) G(197) G(198) G(199) G(201) G(204) G(205) G(206) + G(218) G(219) G(229) G(236) G(237), + G(2) G(1) G(3) G(181) G(0) G(159) G(8) G(10) G(12) + G(29) G(127) G(152) G(160) G(156) +) +OT_SUBLOOKUP_SINGLE_SUBST_FORMAT2(medifinaLamAlefSubLookup, + G(165) G(178) G(180) G(252), + G(170) G(179) G(185) G(255) +) + +/* + * Lam+Alef ligatures + */ +OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(lamAlefLigaturesSubLookup, + G(225), + OT_OFFSET(lamAlefLigaturesSubLookup, lamLigatureSet) +) +OT_LIGATURE_SET(lamLigatureSet, + OT_OFFSET(lamLigatureSet, lamInitLigature1) + OT_OFFSET(lamLigatureSet, lamInitLigature2) + OT_OFFSET(lamLigatureSet, lamInitLigature3) + OT_OFFSET(lamLigatureSet, lamInitLigature4) +) +OT_LIGATURE(lamInitLigature1, G(199), G(165)) +OT_LIGATURE(lamInitLigature2, G(195), G(178)) +OT_LIGATURE(lamInitLigature3, G(194), G(180)) +OT_LIGATURE(lamInitLigature4, G(197), G(252)) + +/* + * Shadda ligatures + */ +OT_SUBLOOKUP_LIGATURE_SUBST_FORMAT1(shaddaLigaturesSubLookup, + G(248), + OT_OFFSET(shaddaLigaturesSubLookup, shaddaLigatureSet) +) +OT_LIGATURE_SET(shaddaLigatureSet, + OT_OFFSET(shaddaLigatureSet, shaddaLigature1) + OT_OFFSET(shaddaLigatureSet, shaddaLigature2) + OT_OFFSET(shaddaLigatureSet, shaddaLigature3) +) +OT_LIGATURE(shaddaLigature1, G(243), G(172)) +OT_LIGATURE(shaddaLigature2, G(245), G(173)) +OT_LIGATURE(shaddaLigature3, G(246), G(175)) + +/* + * Table end + */ +OT_TABLE_END + + +/* + * Clean up + */ +#undef OT_TABLE_START +#undef OT_TABLE_END +#undef OT_LABEL_START +#undef OT_LABEL_END +#undef OT_BYTE +#undef OT_USHORT +#undef OT_DISTANCE +#undef OT_COUNT + +/* + * Include a second time to get the table data... + */ +#ifdef OT_MEASURE +#include __FILE__ +#endif + +#define HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH +#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_WIN1256_HH */