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
41static 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
208void 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
228void 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
341static 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
349HB_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
363HB_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
385const 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
445static 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
457static 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
473enum {
474    RequiresGsub = 1,
475    RequiresGpos = 2
476};
477
478struct OTScripts {
479    unsigned int tag;
480    int flags;
481};
482static 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};
540enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
541
542static 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
583static 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
614HB_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
677void 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
696HB_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
790HB_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
850HB_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
983HB_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}
995