2 * Copyright © 2012 Google, Inc.
4 * This is part of HarfBuzz, a text shaping library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 * Google Author(s): Behdad Esfahbod
27 #ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
28 #define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
30 #include "hb-private.hh"
32 #include "hb-ot-shape-private.hh"
33 #include "hb-ot-layout-gsub-table.hh"
36 static const hb_tag_t arabic_fallback_features
[] =
38 HB_TAG('i','n','i','t'),
39 HB_TAG('m','e','d','i'),
40 HB_TAG('f','i','n','a'),
41 HB_TAG('i','s','o','l'),
42 HB_TAG('r','l','i','g'),
45 /* Same order as the fallback feature array */
52 ARABIC_NUM_FALLBACK_FEATURES
55 static OT::SubstLookup
*
56 arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t
*plan HB_UNUSED
,
58 unsigned int feature_index
)
60 OT::GlyphID glyphs
[SHAPING_TABLE_LAST
- SHAPING_TABLE_FIRST
+ 1];
61 OT::GlyphID substitutes
[SHAPING_TABLE_LAST
- SHAPING_TABLE_FIRST
+ 1];
62 unsigned int num_glyphs
= 0;
65 for (hb_codepoint_t u
= SHAPING_TABLE_FIRST
; u
< SHAPING_TABLE_LAST
+ 1; u
++)
67 hb_codepoint_t s
= shaping_table
[u
- SHAPING_TABLE_FIRST
][feature_index
];
68 hb_codepoint_t u_glyph
, s_glyph
;
71 !hb_font_get_glyph (font
, u
, 0, &u_glyph
) ||
72 !hb_font_get_glyph (font
, s
, 0, &s_glyph
) ||
74 u_glyph
> 0xFFFF || s_glyph
> 0xFFFF)
77 glyphs
[num_glyphs
].set (u_glyph
);
78 substitutes
[num_glyphs
].set (s_glyph
);
84 * May not be good-enough for presidential candidate interviews, but good-enough for us... */
85 hb_bubble_sort (&glyphs
[0], num_glyphs
, OT::GlyphID::cmp
, &substitutes
[0]);
87 OT::Supplier
<OT::GlyphID
> glyphs_supplier (glyphs
, num_glyphs
);
88 OT::Supplier
<OT::GlyphID
> substitutes_supplier (substitutes
, num_glyphs
);
90 /* Each glyph takes four bytes max, and there's some overhead. */
91 char buf
[(SHAPING_TABLE_LAST
- SHAPING_TABLE_FIRST
+ 1) * 4 + 128];
92 OT::hb_serialize_context_t
c (buf
, sizeof (buf
));
93 OT::SubstLookup
*lookup
= c
.start_serialize
<OT::SubstLookup
> ();
94 bool ret
= lookup
->serialize_single (&c
,
95 OT::LookupFlag::IgnoreMarks
,
100 /* TODO sanitize the results? */
102 return ret
? c
.copy
<OT::SubstLookup
> () : NULL
;
105 static OT::SubstLookup
*
106 arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t
*plan HB_UNUSED
,
109 OT::GlyphID first_glyphs
[ARRAY_LENGTH_CONST (ligature_table
)];
110 unsigned int first_glyphs_indirection
[ARRAY_LENGTH_CONST (ligature_table
)];
111 unsigned int ligature_per_first_glyph_count_list
[ARRAY_LENGTH_CONST (first_glyphs
)];
112 unsigned int num_first_glyphs
= 0;
114 /* We know that all our ligatures are 2-component */
115 OT::GlyphID ligature_list
[ARRAY_LENGTH_CONST (first_glyphs
) * ARRAY_LENGTH_CONST(ligature_table
[0].ligatures
)];
116 unsigned int component_count_list
[ARRAY_LENGTH_CONST (ligature_list
)];
117 OT::GlyphID component_list
[ARRAY_LENGTH_CONST (ligature_list
) * 1/* One extra component per ligature */];
118 unsigned int num_ligatures
= 0;
120 /* Populate arrays */
122 /* Sort out the first-glyphs */
123 for (unsigned int first_glyph_idx
= 0; first_glyph_idx
< ARRAY_LENGTH (first_glyphs
); first_glyph_idx
++)
125 hb_codepoint_t first_u
= ligature_table
[first_glyph_idx
].first
;
126 hb_codepoint_t first_glyph
;
127 if (!hb_font_get_glyph (font
, first_u
, 0, &first_glyph
))
129 first_glyphs
[num_first_glyphs
].set (first_glyph
);
130 ligature_per_first_glyph_count_list
[num_first_glyphs
] = 0;
131 first_glyphs_indirection
[num_first_glyphs
] = first_glyph_idx
;
134 hb_bubble_sort (&first_glyphs
[0], num_first_glyphs
, OT::GlyphID::cmp
, &first_glyphs_indirection
[0]);
136 /* Now that the first-glyphs are sorted, walk again, populate ligatures. */
137 for (unsigned int i
= 0; i
< num_first_glyphs
; i
++)
139 unsigned int first_glyph_idx
= first_glyphs_indirection
[i
];
141 for (unsigned int second_glyph_idx
= 0; second_glyph_idx
< ARRAY_LENGTH (ligature_table
[0].ligatures
); second_glyph_idx
++)
143 hb_codepoint_t second_u
= ligature_table
[first_glyph_idx
].ligatures
[second_glyph_idx
].second
;
144 hb_codepoint_t ligature_u
= ligature_table
[first_glyph_idx
].ligatures
[second_glyph_idx
].ligature
;
145 hb_codepoint_t second_glyph
, ligature_glyph
;
147 !hb_font_get_glyph (font
, second_u
, 0, &second_glyph
) ||
148 !hb_font_get_glyph (font
, ligature_u
, 0, &ligature_glyph
))
151 ligature_per_first_glyph_count_list
[i
]++;
153 ligature_list
[num_ligatures
].set (ligature_glyph
);
154 component_count_list
[num_ligatures
] = 2;
155 component_list
[num_ligatures
].set (second_glyph
);
160 OT::Supplier
<OT::GlyphID
> first_glyphs_supplier (first_glyphs
, num_first_glyphs
);
161 OT::Supplier
<unsigned int > ligature_per_first_glyph_count_supplier (ligature_per_first_glyph_count_list
, num_first_glyphs
);
162 OT::Supplier
<OT::GlyphID
> ligatures_supplier (ligature_list
, num_ligatures
);
163 OT::Supplier
<unsigned int > component_count_supplier (component_count_list
, num_ligatures
);
164 OT::Supplier
<OT::GlyphID
> component_supplier (component_list
, num_ligatures
);
166 /* 16 bytes per ligature ought to be enough... */
167 char buf
[ARRAY_LENGTH_CONST (ligature_list
) * 16 + 128];
168 OT::hb_serialize_context_t
c (buf
, sizeof (buf
));
169 OT::SubstLookup
*lookup
= c
.start_serialize
<OT::SubstLookup
> ();
170 bool ret
= lookup
->serialize_ligature (&c
,
171 OT::LookupFlag::IgnoreMarks
,
172 first_glyphs_supplier
,
173 ligature_per_first_glyph_count_supplier
,
176 component_count_supplier
,
180 /* TODO sanitize the results? */
182 return ret
? c
.copy
<OT::SubstLookup
> () : NULL
;
185 static OT::SubstLookup
*
186 arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t
*plan
,
188 unsigned int feature_index
)
190 if (feature_index
< 4)
191 return arabic_fallback_synthesize_lookup_single (plan
, font
, feature_index
);
193 return arabic_fallback_synthesize_lookup_ligature (plan
, font
);
196 struct arabic_fallback_plan_t
200 hb_mask_t mask_array
[ARABIC_NUM_FALLBACK_FEATURES
];
201 OT::SubstLookup
*lookup_array
[ARABIC_NUM_FALLBACK_FEATURES
];
202 hb_ot_layout_lookup_accelerator_t accel_array
[ARABIC_NUM_FALLBACK_FEATURES
];
205 static const arabic_fallback_plan_t arabic_fallback_plan_nil
= {};
207 static arabic_fallback_plan_t
*
208 arabic_fallback_plan_create (const hb_ot_shape_plan_t
*plan
,
211 arabic_fallback_plan_t
*fallback_plan
= (arabic_fallback_plan_t
*) calloc (1, sizeof (arabic_fallback_plan_t
));
212 if (unlikely (!fallback_plan
))
213 return const_cast<arabic_fallback_plan_t
*> (&arabic_fallback_plan_nil
);
215 for (unsigned int i
= 0; i
< ARABIC_NUM_FALLBACK_FEATURES
; i
++)
217 fallback_plan
->mask_array
[i
] = plan
->map
.get_1_mask (arabic_fallback_features
[i
]);
218 if (fallback_plan
->mask_array
[i
]) {
219 fallback_plan
->lookup_array
[i
] = arabic_fallback_synthesize_lookup (plan
, font
, i
);
220 if (fallback_plan
->lookup_array
[i
])
221 fallback_plan
->accel_array
[i
].init (*fallback_plan
->lookup_array
[i
]);
225 return fallback_plan
;
229 arabic_fallback_plan_destroy (arabic_fallback_plan_t
*fallback_plan
)
231 if (!fallback_plan
|| fallback_plan
== &arabic_fallback_plan_nil
)
234 for (unsigned int i
= 0; i
< ARABIC_NUM_FALLBACK_FEATURES
; i
++)
235 if (fallback_plan
->lookup_array
[i
])
237 fallback_plan
->accel_array
[i
].fini (fallback_plan
->lookup_array
[i
]);
238 free (fallback_plan
->lookup_array
[i
]);
241 free (fallback_plan
);
245 arabic_fallback_plan_shape (arabic_fallback_plan_t
*fallback_plan
,
249 OT::hb_apply_context_t
c (0, font
, buffer
);
250 for (unsigned int i
= 0; i
< ARABIC_NUM_FALLBACK_FEATURES
; i
++)
251 if (fallback_plan
->lookup_array
[i
]) {
252 c
.set_lookup_mask (fallback_plan
->mask_array
[i
]);
253 hb_ot_layout_substitute_lookup (&c
,
254 *fallback_plan
->lookup_array
[i
],
255 fallback_plan
->accel_array
[i
]);
260 #endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */