d1e23355f554bf31c31c82dcdcf7b9c15149c7fd
[platform/upstream/harfbuzz.git] / src / hb-old / harfbuzz-shaper.cpp
1 /*
2  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3  *
4  * This is part of HarfBuzz, an OpenType Layout engine library.
5  *
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.
11  *
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
16  * DAMAGE.
17  *
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.
23  */
24
25 #include "harfbuzz-shaper.h"
26 #include "harfbuzz-shaper-private.h"
27
28 #include "harfbuzz-stream-private.h"
29 #include <assert.h>
30 #include <stdio.h>
31
32 #define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
33 #define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
34
35 // --------------------------------------------------------------------------------------------------------------------------------------------
36 //
37 // Basic processing
38 //
39 // --------------------------------------------------------------------------------------------------------------------------------------------
40
41 static inline void positionCluster(HB_ShaperItem *item, int gfrom,  int glast)
42 {
43     int nmarks = glast - gfrom;
44     assert(nmarks > 0);
45
46     HB_Glyph *glyphs = item->glyphs;
47     HB_GlyphAttributes *attributes = item->attributes;
48
49     HB_GlyphMetrics baseMetrics;
50     item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
51
52     if (item->item.script == HB_Script_Hebrew
53         && (-baseMetrics.y) > baseMetrics.height)
54         // we need to attach below the baseline, because of the hebrew iud.
55         baseMetrics.height = -baseMetrics.y;
56
57 //     qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
58 //     qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
59
60     HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
61     HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
62     if (size > HB_FIXED_CONSTANT(4))
63         offsetBase += HB_FIXED_CONSTANT(4);
64     else
65         offsetBase += size;
66     offsetBase = -offsetBase;
67     //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
68 //     qDebug("offset = %f", offsetBase);
69
70     bool rightToLeft = item->item.bidiLevel % 2;
71
72     int i;
73     unsigned char lastCmb = 0;
74     HB_GlyphMetrics attachmentRect;
75     memset(&attachmentRect, 0, sizeof(attachmentRect));
76
77     for(i = 1; i <= nmarks; i++) {
78         HB_Glyph mark = glyphs[gfrom+i];
79         HB_GlyphMetrics markMetrics;
80         item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
81         HB_FixedPoint p;
82         p.x = p.y = 0;
83 //          qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
84
85         HB_Fixed offset = offsetBase;
86         unsigned char cmb = attributes[gfrom+i].combiningClass;
87
88         // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
89         // bits  in the glyphAttributes structure.
90         if (cmb < 200) {
91             // fixed position classes. We approximate by mapping to one of the others.
92             // currently I added only the ones for arabic, hebrew, lao and thai.
93
94             // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
95
96             // add a bit more offset to arabic, a bit hacky
97             if (cmb >= 27 && cmb <= 36 && offset < 3)
98                 offset +=1;
99             // below
100             if ((cmb >= 10 && cmb <= 18) ||
101                  cmb == 20 || cmb == 22 ||
102                  cmb == 29 || cmb == 32)
103                 cmb = HB_Combining_Below;
104             // above
105             else if (cmb == 23 || cmb == 27 || cmb == 28 ||
106                       cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
107                 cmb = HB_Combining_Above;
108             //below-right
109             else if (cmb == 9 || cmb == 103 || cmb == 118)
110                 cmb = HB_Combining_BelowRight;
111             // above-right
112             else if (cmb == 24 || cmb == 107 || cmb == 122)
113                 cmb = HB_Combining_AboveRight;
114             else if (cmb == 25)
115                 cmb = HB_Combining_AboveLeft;
116             // fixed:
117             //  19 21
118
119         }
120
121         // combining marks of different class don't interact. Reset the rectangle.
122         if (cmb != lastCmb) {
123             //qDebug("resetting rect");
124             attachmentRect = baseMetrics;
125         }
126
127         switch(cmb) {
128         case HB_Combining_DoubleBelow:
129                 // ### wrong in rtl context!
130         case HB_Combining_BelowLeft:
131             p.y += offset;
132         case HB_Combining_BelowLeftAttached:
133             p.x += attachmentRect.x - markMetrics.x;
134             p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
135             break;
136         case HB_Combining_Below:
137             p.y += offset;
138         case HB_Combining_BelowAttached:
139             p.x += attachmentRect.x - markMetrics.x;
140             p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
141
142             p.x += (attachmentRect.width - markMetrics.width) / 2;
143             break;
144         case HB_Combining_BelowRight:
145             p.y += offset;
146         case HB_Combining_BelowRightAttached:
147             p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
148             p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
149             break;
150         case HB_Combining_Left:
151             p.x -= offset;
152         case HB_Combining_LeftAttached:
153             break;
154         case HB_Combining_Right:
155             p.x += offset;
156         case HB_Combining_RightAttached:
157             break;
158         case HB_Combining_DoubleAbove:
159             // ### wrong in RTL context!
160         case HB_Combining_AboveLeft:
161             p.y -= offset;
162         case HB_Combining_AboveLeftAttached:
163             p.x += attachmentRect.x - markMetrics.x;
164             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
165             break;
166         case HB_Combining_Above:
167             p.y -= offset;
168         case HB_Combining_AboveAttached:
169             p.x += attachmentRect.x - markMetrics.x;
170             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
171
172             p.x += (attachmentRect.width - markMetrics.width) / 2;
173             break;
174         case HB_Combining_AboveRight:
175             p.y -= offset;
176         case HB_Combining_AboveRightAttached:
177             p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
178             p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
179             break;
180
181         case HB_Combining_IotaSubscript:
182             default:
183                 break;
184         }
185 //          qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
186         markMetrics.x += p.x;
187         markMetrics.y += p.y;
188
189         HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
190         unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
191         unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
192         unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
193         unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
194         attachmentRect = unitedAttachmentRect;
195
196         lastCmb = cmb;
197         if (rightToLeft) {
198             item->offsets[gfrom+i].x = p.x;
199             item->offsets[gfrom+i].y = p.y;
200         } else {
201             item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
202             item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
203         }
204         item->advances[gfrom+i] = 0;
205     }
206 }
207
208 void HB_HeuristicPosition(HB_ShaperItem *item)
209 {
210     HB_GetGlyphAdvances(item);
211     HB_GlyphAttributes *attributes = item->attributes;
212
213     int cEnd = -1;
214     int i = item->num_glyphs;
215     while (i--) {
216         if (cEnd == -1 && attributes[i].mark) {
217             cEnd = i;
218         } else if (cEnd != -1 && !attributes[i].mark) {
219             positionCluster(item, i, cEnd);
220             cEnd = -1;
221         }
222     }
223 }
224
225 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
226 // and no reordering.
227 // also computes logClusters heuristically
228 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
229 {
230     const HB_UChar16 *uc = item->string + item->item.pos;
231     hb_uint32 length = item->item.length;
232
233     // ### zeroWidth and justification are missing here!!!!!
234
235     assert(item->num_glyphs <= length);
236
237 //     qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
238     HB_GlyphAttributes *attributes = item->attributes;
239     unsigned short *logClusters = item->log_clusters;
240
241     hb_uint32 glyph_pos = 0;
242     hb_uint32 i;
243     for (i = 0; i < length; i++) {
244         if (HB_IsHighSurrogate(uc[i]) && i < length - 1
245             && HB_IsLowSurrogate(uc[i + 1])) {
246             logClusters[i] = glyph_pos;
247             logClusters[++i] = glyph_pos;
248         } else {
249             logClusters[i] = glyph_pos;
250         }
251         ++glyph_pos;
252     }
253     assert(glyph_pos == item->num_glyphs);
254
255     // first char in a run is never (treated as) a mark
256     int cStart = 0;
257     const bool symbolFont = item->face->isSymbolFont;
258     attributes[0].mark = false;
259     attributes[0].clusterStart = true;
260     attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
261
262     int pos = 0;
263     HB_CharCategory lastCat;
264     int dummy;
265     HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
266     for (i = 1; i < length; ++i) {
267         if (logClusters[i] == pos)
268             // same glyph
269             continue;
270         ++pos;
271         while (pos < logClusters[i]) {
272             attributes[pos] = attributes[pos-1];
273             ++pos;
274         }
275         // hide soft-hyphens by default
276         if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
277             attributes[pos].dontPrint = true;
278         HB_CharCategory cat;
279         int cmb;
280         HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
281         if (cat != HB_Mark_NonSpacing) {
282             attributes[pos].mark = false;
283             attributes[pos].clusterStart = true;
284             attributes[pos].combiningClass = 0;
285             cStart = logClusters[i];
286         } else {
287             if (cmb == 0) {
288                 // Fix 0 combining classes
289                 if ((uc[pos] & 0xff00) == 0x0e00) {
290                     // thai or lao
291                     if (uc[pos] == 0xe31 ||
292                          uc[pos] == 0xe34 ||
293                          uc[pos] == 0xe35 ||
294                          uc[pos] == 0xe36 ||
295                          uc[pos] == 0xe37 ||
296                          uc[pos] == 0xe47 ||
297                          uc[pos] == 0xe4c ||
298                          uc[pos] == 0xe4d ||
299                          uc[pos] == 0xe4e) {
300                         cmb = HB_Combining_AboveRight;
301                     } else if (uc[pos] == 0xeb1 ||
302                                 uc[pos] == 0xeb4 ||
303                                 uc[pos] == 0xeb5 ||
304                                 uc[pos] == 0xeb6 ||
305                                 uc[pos] == 0xeb7 ||
306                                 uc[pos] == 0xebb ||
307                                 uc[pos] == 0xecc ||
308                                 uc[pos] == 0xecd) {
309                         cmb = HB_Combining_Above;
310                     } else if (uc[pos] == 0xebc) {
311                         cmb = HB_Combining_Below;
312                     }
313                 }
314             }
315
316             attributes[pos].mark = true;
317             attributes[pos].clusterStart = false;
318             attributes[pos].combiningClass = cmb;
319             logClusters[i] = cStart;
320         }
321         // one gets an inter character justification point if the current char is not a non spacing mark.
322         // as then the current char belongs to the last one and one gets a space justification point
323         // after the space char.
324         if (lastCat == HB_Separator_Space)
325             attributes[pos-1].justification = HB_Space;
326         else if (cat != HB_Mark_NonSpacing)
327             attributes[pos-1].justification = HB_Character;
328         else
329             attributes[pos-1].justification = HB_NoJustification;
330
331         lastCat = cat;
332     }
333     pos = logClusters[length-1];
334     if (lastCat == HB_Separator_Space)
335         attributes[pos].justification = HB_Space;
336     else
337         attributes[pos].justification = HB_Character;
338 }
339
340 #ifndef NO_OPENTYPE
341 static const HB_OpenTypeFeature basic_features[] = {
342     { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
343     { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
344     { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
345     {0, 0}
346 };
347 #endif
348
349 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
350 {
351     if (shaper_item->glyphIndicesPresent) {
352         shaper_item->num_glyphs = shaper_item->initialGlyphCount;
353         shaper_item->glyphIndicesPresent = false;
354         return true;
355     }
356     return shaper_item->font->klass
357            ->convertStringToGlyphIndices(shaper_item->font,
358                                          shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
359                                          shaper_item->glyphs, &shaper_item->num_glyphs,
360                                          shaper_item->item.bidiLevel % 2);
361 }
362
363 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
364 {
365 #ifndef NO_OPENTYPE
366     const int availableGlyphs = shaper_item->num_glyphs;
367 #endif
368
369     if (!HB_ConvertStringToGlyphIndices(shaper_item))
370         return false;
371
372     HB_HeuristicSetGlyphAttributes(shaper_item);
373
374 #ifndef NO_OPENTYPE
375     if (HB_SelectScript(shaper_item, basic_features)) {
376         HB_OpenTypeShape(shaper_item, /*properties*/0);
377         return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
378     }
379 #endif
380
381     HB_HeuristicPosition(shaper_item);
382     return true;
383 }
384
385 const HB_ScriptEngine HB_ScriptEngines[] = {
386     // Common
387     { HB_BasicShape},
388     // Greek
389     { HB_GreekShape},
390     // Cyrillic
391     { HB_BasicShape},
392     // Armenian
393     { HB_BasicShape},
394     // Hebrew
395     { HB_HebrewShape},
396     // Arabic
397     { HB_ArabicShape},
398     // Syriac
399     { HB_ArabicShape},
400     // Thaana
401     { HB_BasicShape},
402     // Devanagari
403     { HB_IndicShape},
404     // Bengali
405     { HB_IndicShape},
406     // Gurmukhi
407     { HB_IndicShape},
408     // Gujarati
409     { HB_IndicShape},
410     // Oriya
411     { HB_IndicShape},
412     // Tamil
413     { HB_IndicShape},
414     // Telugu
415     { HB_IndicShape},
416     // Kannada
417     { HB_IndicShape},
418     // Malayalam
419     { HB_IndicShape},
420     // Sinhala
421     { HB_IndicShape},
422     // Thai
423     { HB_BasicShape},
424     // Lao
425     { HB_BasicShape},
426     // Tibetan
427     { HB_TibetanShape},
428     // Myanmar
429     { HB_MyanmarShape},
430     // Georgian
431     { HB_BasicShape},
432     // Hangul
433     { HB_HangulShape},
434     // Ogham
435     { HB_BasicShape},
436     // Runic
437     { HB_BasicShape},
438     // Khmer
439     { HB_KhmerShape},
440     // N'Ko
441     { HB_ArabicShape}
442 };
443
444
445 static inline char *tag_to_string(HB_UInt tag)
446 {
447     static char string[5];
448     string[0] = (tag >> 24)&0xff;
449     string[1] = (tag >> 16)&0xff;
450     string[2] = (tag >> 8)&0xff;
451     string[3] = tag&0xff;
452     string[4] = 0;
453     return string;
454 }
455
456 #ifdef OT_DEBUG
457 static void dump_string(HB_Buffer buffer)
458 {
459     for (uint i = 0; i < buffer->in_length; ++i) {
460         qDebug("    %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
461     }
462 }
463 #define DEBUG printf
464 #else
465 #define DEBUG if (1) ; else printf
466 #endif
467
468 #if 0
469 #define DefaultLangSys 0xffff
470 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
471 #endif
472
473 enum {
474     RequiresGsub = 1,
475     RequiresGpos = 2
476 };
477
478 struct OTScripts {
479     unsigned int tag;
480     int flags;
481 };
482 static const OTScripts ot_scripts [] = {
483     // Common
484     { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
485     // Greek
486     { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
487     // Cyrillic
488     { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
489     // Armenian
490     { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
491     // Hebrew
492     { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
493     // Arabic
494     { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
495     // Syriac
496     { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
497     // Thaana
498     { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
499     // Devanagari
500     { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
501     // Bengali
502     { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
503     // Gurmukhi
504     { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
505     // Gujarati
506     { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
507     // Oriya
508     { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
509     // Tamil
510     { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
511     // Telugu
512     { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
513     // Kannada
514     { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
515     // Malayalam
516     { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
517     // Sinhala
518     { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
519     // Thai
520     { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
521     // Lao
522     { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
523     // Tibetan
524     { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
525     // Myanmar
526     { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
527     // Georgian
528     { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
529     // Hangul
530     { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
531     // Ogham
532     { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
533     // Runic
534     { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
535     // Khmer
536     { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
537     // N'Ko
538     { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
539 };
540 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
541
542 static HB_Bool checkScript(HB_Face face, int script)
543 {
544     assert(script < HB_ScriptCount);
545
546     if (!face->gsub && !face->gpos)
547         return false;
548
549     unsigned int tag = ot_scripts[script].tag;
550     int requirements = ot_scripts[script].flags;
551
552     if (requirements & RequiresGsub) {
553         if (!face->gsub)
554             return false;
555
556         HB_UShort script_index;
557         HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
558         if (error) {
559             DEBUG("could not select script %d in GSub table: %d", (int)script, error);
560             error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
561             if (error)
562                 return false;
563         }
564     }
565
566     if (requirements & RequiresGpos) {
567         if (!face->gpos)
568             return false;
569
570         HB_UShort script_index;
571         HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
572         if (error) {
573             DEBUG("could not select script in gpos table: %d", error);
574             error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
575             if (error)
576                 return false;
577         }
578
579     }
580     return true;
581 }
582
583 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
584 {
585     HB_Error error;
586     HB_UInt length = 0;
587     HB_Stream stream = 0;
588
589     if (!font)
590         return 0;
591
592     error = tableFunc(font, tag, 0, &length);
593     if (error)
594         return 0;
595     stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
596     if (!stream)
597         return 0;
598     stream->base = (HB_Byte*)malloc(length);
599     if (!stream->base) {
600         free(stream);
601         return 0;
602     }
603     error = tableFunc(font, tag, stream->base, &length);
604     if (error) {
605         _hb_close_stream(stream);
606         return 0;
607     }
608     stream->size = length;
609     stream->pos = 0;
610     stream->cursor = NULL;
611     return stream;
612 }
613
614 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
615 {
616     HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
617     if (!face)
618         return 0;
619
620     face->isSymbolFont = false;
621     face->gdef = 0;
622     face->gpos = 0;
623     face->gsub = 0;
624     face->current_script = HB_ScriptCount;
625     face->current_flags = HB_ShaperFlag_Default;
626     face->has_opentype_kerning = false;
627     face->tmpAttributes = 0;
628     face->tmpLogClusters = 0;
629     face->glyphs_substituted = false;
630     face->buffer = 0;
631
632     HB_Error error = HB_Err_Ok;
633     HB_Stream stream;
634     HB_Stream gdefStream;
635
636     gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
637     error = HB_Err_Not_Covered;
638     if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
639         //DEBUG("error loading gdef table: %d", error);
640         face->gdef = 0;
641     }
642
643     //DEBUG() << "trying to load gsub table";
644     stream = getTableStream(font, tableFunc, TTAG_GSUB);
645     error = HB_Err_Not_Covered;
646     if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
647         face->gsub = 0;
648         if (error != HB_Err_Not_Covered) {
649             //DEBUG("error loading gsub table: %d", error);
650         } else {
651             //DEBUG("face doesn't have a gsub table");
652         }
653     }
654     _hb_close_stream(stream);
655
656     stream = getTableStream(font, tableFunc, TTAG_GPOS);
657     error = HB_Err_Not_Covered;
658     if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
659         face->gpos = 0;
660         DEBUG("error loading gpos table: %d", error);
661     }
662     _hb_close_stream(stream);
663
664     _hb_close_stream(gdefStream);
665
666     for (unsigned int i = 0; i < HB_ScriptCount; ++i)
667         face->supported_scripts[i] = checkScript(face, i);
668
669     if (HB_Buffer_new(&face->buffer) != HB_Err_Ok) {
670         HB_FreeFace(face);
671         return 0;
672     }
673
674     return face;
675 }
676
677 void HB_FreeFace(HB_Face face)
678 {
679     if (!face)
680         return;
681     if (face->gpos)
682         HB_Done_GPOS_Table(face->gpos);
683     if (face->gsub)
684         HB_Done_GSUB_Table(face->gsub);
685     if (face->gdef)
686         HB_Done_GDEF_Table(face->gdef);
687     if (face->buffer)
688         HB_Buffer_free(face->buffer);
689     if (face->tmpAttributes)
690         free(face->tmpAttributes);
691     if (face->tmpLogClusters)
692         free(face->tmpLogClusters);
693     free(face);
694 }
695
696 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
697 {
698     HB_Script script = shaper_item->item.script;
699
700     if (!shaper_item->face->supported_scripts[script])
701         return false;
702
703     HB_Face face = shaper_item->face;
704     if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
705         return true;
706
707     face->current_script = script;
708     face->current_flags = shaper_item->shaperFlags;
709
710     assert(script < HB_ScriptCount);
711     // find script in our list of supported scripts.
712     unsigned int tag = ot_scripts[script].tag;
713
714     if (face->gsub && features) {
715 #ifdef OT_DEBUG
716         {
717             HB_FeatureList featurelist = face->gsub->FeatureList;
718             int numfeatures = featurelist.FeatureCount;
719             DEBUG("gsub table has %d features", numfeatures);
720             for (int i = 0; i < numfeatures; i++) {
721                 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
722                 DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
723             }
724         }
725 #endif
726         HB_GSUB_Clear_Features(face->gsub);
727         HB_UShort script_index;
728         HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
729         if (!error) {
730             DEBUG("script %s has script index %d", tag_to_string(script), script_index);
731             while (features->tag) {
732                 HB_UShort feature_index;
733                 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
734                 if (!error) {
735                     DEBUG("  adding feature %s", tag_to_string(features->tag));
736                     HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
737                 }
738                 ++features;
739             }
740         }
741     }
742
743     // reset
744     face->has_opentype_kerning = false;
745
746     if (face->gpos) {
747         HB_GPOS_Clear_Features(face->gpos);
748         HB_UShort script_index;
749         HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
750         if (!error) {
751 #ifdef OT_DEBUG
752             {
753                 HB_FeatureList featurelist = face->gpos->FeatureList;
754                 int numfeatures = featurelist.FeatureCount;
755                 DEBUG("gpos table has %d features", numfeatures);
756                 for(int i = 0; i < numfeatures; i++) {
757                     HB_FeatureRecord *r = featurelist.FeatureRecord + i;
758                     HB_UShort feature_index;
759                     HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
760                     DEBUG("   feature '%s'", tag_to_string(r->FeatureTag));
761                 }
762             }
763 #endif
764             HB_UInt *feature_tag_list_buffer;
765             error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
766             if (!error) {
767                 HB_UInt *feature_tag_list = feature_tag_list_buffer;
768                 while (*feature_tag_list) {
769                     HB_UShort feature_index;
770                     if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
771                         if (face->current_flags & HB_ShaperFlag_NoKerning) {
772                             ++feature_tag_list;
773                             continue;
774                         }
775                         face->has_opentype_kerning = true;
776                     }
777                     error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
778                     if (!error)
779                         HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
780                     ++feature_tag_list;
781                 }
782                 FREE(feature_tag_list_buffer);
783             }
784         }
785     }
786
787     return true;
788 }
789
790 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
791 {
792     HB_GlyphAttributes *tmpAttributes;
793     unsigned int *tmpLogClusters;
794
795     HB_Face face = item->face;
796
797     face->length = item->num_glyphs;
798
799     HB_Buffer_clear(face->buffer);
800
801     tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
802     if (!tmpAttributes)
803         return false;
804     face->tmpAttributes = tmpAttributes;
805
806     tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
807     if (!tmpLogClusters)
808         return false;
809     face->tmpLogClusters = tmpLogClusters;
810
811     for (int i = 0; i < face->length; ++i) {
812         HB_Buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
813         face->tmpAttributes[i] = item->attributes[i];
814         face->tmpLogClusters[i] = item->log_clusters[i];
815     }
816
817 #ifdef OT_DEBUG
818     DEBUG("-----------------------------------------");
819 //     DEBUG("log clusters before shaping:");
820 //     for (int j = 0; j < length; j++)
821 //         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
822     DEBUG("original glyphs: %p", item->glyphs);
823     for (int i = 0; i < length; ++i)
824         DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
825 //     dump_string(hb_buffer);
826 #endif
827
828     face->glyphs_substituted = false;
829     if (face->gsub) {
830         unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
831         if (error && error != HB_Err_Not_Covered)
832             return false;
833         face->glyphs_substituted = (error != HB_Err_Not_Covered);
834     }
835
836 #ifdef OT_DEBUG
837 //     DEBUG("log clusters before shaping:");
838 //     for (int j = 0; j < length; j++)
839 //         DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
840     DEBUG("shaped glyphs:");
841     for (int i = 0; i < length; ++i)
842         DEBUG("   glyph=%4x", hb_buffer->in_string[i].gindex);
843     DEBUG("-----------------------------------------");
844 //     dump_string(hb_buffer);
845 #endif
846
847     return true;
848 }
849
850 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
851 {
852     HB_Face face = item->face;
853
854     bool glyphs_positioned = false;
855     if (face->gpos) {
856         if (face->buffer->positions)
857             memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
858         // #### check that passing "false,false" is correct
859         glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
860     }
861
862     if (!face->glyphs_substituted && !glyphs_positioned) {
863         HB_GetGlyphAdvances(item);
864         return true; // nothing to do for us
865     }
866
867     // make sure we have enough space to write everything back
868     if (availableGlyphs < (int)face->buffer->in_length) {
869         item->num_glyphs = face->buffer->in_length;
870         return false;
871     }
872
873     HB_Glyph *glyphs = item->glyphs;
874     HB_GlyphAttributes *attributes = item->attributes;
875
876     for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
877         glyphs[i] = face->buffer->in_string[i].gindex;
878         attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
879         if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
880             attributes[i].clusterStart = false;
881     }
882     item->num_glyphs = face->buffer->in_length;
883
884     if (doLogClusters && face->glyphs_substituted) {
885         // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
886         unsigned short *logClusters = item->log_clusters;
887         int clusterStart = 0;
888         int oldCi = 0;
889         // #### the reconstruction of the logclusters currently does not work if the original string
890         // contains surrogate pairs
891         for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
892             int ci = face->buffer->in_string[i].cluster;
893             //         DEBUG("   ci[%d] = %d mark=%d, cmb=%d, cs=%d",
894             //                i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
895             if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
896                 for (int j = oldCi; j < ci; j++)
897                     logClusters[j] = clusterStart;
898                 clusterStart = i;
899                 oldCi = ci;
900             }
901         }
902         for (int j = oldCi; j < face->length; j++)
903             logClusters[j] = clusterStart;
904     }
905
906     // calulate the advances for the shaped glyphs
907 //     DEBUG("unpositioned: ");
908
909     // positioning code:
910     if (glyphs_positioned) {
911         HB_GetGlyphAdvances(item);
912         HB_Position positions = face->buffer->positions;
913         HB_Fixed *advances = item->advances;
914
915 //         DEBUG("positioned glyphs:");
916         for (unsigned int i = 0; i < face->buffer->in_length; i++) {
917 //             DEBUG("    %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
918 //                    glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
919 //                    (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
920 //                    (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
921 //                    positions[i].back, positions[i].new_advance);
922
923             HB_Fixed adjustment = positions[i].x_advance;
924
925             if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
926                 adjustment = HB_FIXED_ROUND(adjustment);
927
928             if (positions[i].new_advance) {
929                 ; //advances[i] = adjustment;
930             } else {
931                 advances[i] += adjustment;
932             }
933
934             int back = 0;
935             HB_FixedPoint *offsets = item->offsets;
936             offsets[i].x = positions[i].x_pos;
937             offsets[i].y = positions[i].y_pos;
938             while (positions[i - back].back) {
939                 back += positions[i - back].back;
940                 offsets[i].x += positions[i - back].x_pos;
941                 offsets[i].y += positions[i - back].y_pos;
942             }
943             offsets[i].y = -offsets[i].y;
944
945             if (item->item.bidiLevel % 2) {
946                 // ### may need to go back multiple glyphs like in ltr
947                 back = positions[i].back;
948                 while (back--)
949                     offsets[i].x -= advances[i-back];
950             } else {
951                 back = 0;
952                 while (positions[i - back].back) {
953                     back += positions[i - back].back;
954                     offsets[i].x -= advances[i-back];
955                 }
956             }
957 //             DEBUG("   ->\tadv=%d\tpos=(%d/%d)",
958 //                    glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
959         }
960         item->kerning_applied = face->has_opentype_kerning;
961     } else {
962         HB_HeuristicPosition(item);
963     }
964
965 #ifdef OT_DEBUG
966     if (doLogClusters) {
967         DEBUG("log clusters after shaping:");
968         for (int j = 0; j < length; j++)
969             DEBUG("    log[%d] = %d", j, item->log_clusters[j]);
970     }
971     DEBUG("final glyphs:");
972     for (int i = 0; i < (int)hb_buffer->in_length; ++i)
973         DEBUG("   glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
974                glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
975                glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
976                glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
977                glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
978     DEBUG("-----------------------------------------");
979 #endif
980     return true;
981 }
982
983 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
984 {
985     HB_Bool result = false;
986     if (shaper_item->num_glyphs < shaper_item->item.length) {
987         shaper_item->num_glyphs = shaper_item->item.length;
988         return false;
989     }
990     assert(shaper_item->item.script < HB_ScriptCount);
991     result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
992     shaper_item->glyphIndicesPresent = false;
993     return result;
994 }