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#include <assert.h>
28
29/*
30// Uniscribe also defines dlig for Hebrew, but we leave this out for now, as it's mostly
31// ligatures one does not want in modern Hebrew (as lam-alef ligatures).
32*/
33#ifndef NO_OPENTYPE
34static const HB_OpenTypeFeature hebrew_features[] = {
35    { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
36    {0, 0}
37};
38#endif
39
40/* Hebrew shaping. In the non opentype case we try to use the
41   presentation forms specified for Hebrew. Especially for the
42   ligatures with Dagesh this gives much better results than we could
43   achieve manually.
44*/
45HB_Bool HB_HebrewShape(HB_ShaperItem *shaper_item)
46{
47    enum {
48        Dagesh = 0x5bc,
49        ShinDot = 0x5c1,
50        SinDot = 0x5c2,
51        Patah = 0x5b7,
52        Qamats = 0x5b8,
53        Holam = 0x5b9,
54        Rafe = 0x5bf
55    };
56
57    assert(shaper_item->item.script == HB_Script_Hebrew);
58
59#ifndef NO_OPENTYPE
60    if (HB_SelectScript(shaper_item, hebrew_features)) {
61
62        const int availableGlyphs = shaper_item->num_glyphs;
63        if (!HB_ConvertStringToGlyphIndices(shaper_item))
64            return FALSE;
65
66        HB_HeuristicSetGlyphAttributes(shaper_item);
67        HB_OpenTypeShape(shaper_item, /*properties*/0);
68        return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/TRUE);
69    }
70#endif
71
72    {
73        const HB_UChar16 *uc = shaper_item->string + shaper_item->item.pos;
74        unsigned short *logClusters = shaper_item->log_clusters;
75        HB_GlyphAttributes *attributes = shaper_item->attributes;
76
77        HB_Bool haveGlyphs;
78        int slen = 1;
79        int cluster_start = 0;
80        hb_uint32 i;
81
82        HB_STACKARRAY(HB_UChar16, shapedChars, 2 * shaper_item->item.length);
83        *shapedChars = *uc;
84        logClusters[0] = 0;
85
86        for (i = 1; i < shaper_item->item.length; ++i) {
87            hb_uint16 base = shapedChars[cluster_start];
88            hb_uint16 shaped = 0;
89            HB_Bool invalid = FALSE;
90            if (uc[i] == Dagesh) {
91                if (base >= 0x5d0
92                    && base <= 0x5ea
93                    && base != 0x5d7
94                    && base != 0x5dd
95                    && base != 0x5df
96                    && base != 0x5e2
97                    && base != 0x5e5) {
98                    shaped = base - 0x5d0 + 0xfb30;
99                } else if (base == 0xfb2a || base == 0xfb2b /* Shin with Shin or Sin dot */) {
100                    shaped = base + 2;
101                } else {
102                    invalid = TRUE;
103                }
104            } else if (uc[i] == ShinDot) {
105                if (base == 0x05e9)
106                    shaped = 0xfb2a;
107                else if (base == 0xfb49)
108                    shaped = 0xfb2c;
109                else
110                    invalid = TRUE;
111            } else if (uc[i] == SinDot) {
112                if (base == 0x05e9)
113                    shaped = 0xfb2b;
114                else if (base == 0xfb49)
115                    shaped = 0xfb2d;
116                else
117                    invalid = TRUE;
118            } else if (uc[i] == Patah) {
119                if (base == 0x5d0)
120                    shaped = 0xfb2e;
121            } else if (uc[i] == Qamats) {
122                if (base == 0x5d0)
123                    shaped = 0xfb2f;
124            } else if (uc[i] == Holam) {
125                if (base == 0x5d5)
126                    shaped = 0xfb4b;
127            } else if (uc[i] == Rafe) {
128                if (base == 0x5d1)
129                    shaped = 0xfb4c;
130                else if (base == 0x5db)
131                    shaped = 0xfb4d;
132                else if (base == 0x5e4)
133                    shaped = 0xfb4e;
134            }
135
136            if (invalid) {
137                shapedChars[slen] = 0x25cc;
138                attributes[slen].clusterStart = TRUE;
139                attributes[slen].mark = FALSE;
140                attributes[slen].combiningClass = 0;
141                cluster_start = slen;
142                ++slen;
143            }
144            if (shaped) {
145                if (shaper_item->font->klass->canRender(shaper_item->font, (HB_UChar16 *)&shaped, 1)) {
146                    shapedChars[cluster_start] = shaped;
147                } else
148                    shaped = 0;
149            }
150            if (!shaped) {
151                HB_CharCategory category;
152                int cmb;
153                shapedChars[slen] = uc[i];
154                HB_GetUnicodeCharProperties(uc[i], &category, &cmb);
155                if (category != HB_Mark_NonSpacing) {
156                    attributes[slen].clusterStart = TRUE;
157                    attributes[slen].mark = FALSE;
158                    attributes[slen].combiningClass = 0;
159                    attributes[slen].dontPrint = HB_IsControlChar(uc[i]);
160                    cluster_start = slen;
161                } else {
162                    attributes[slen].clusterStart = FALSE;
163                    attributes[slen].mark = TRUE;
164                    attributes[slen].combiningClass = cmb;
165                }
166                ++slen;
167            }
168            logClusters[i] = cluster_start;
169        }
170
171        haveGlyphs = shaper_item->font->klass
172            ->convertStringToGlyphIndices(shaper_item->font,
173                                          shapedChars, slen,
174                                          shaper_item->glyphs, &shaper_item->num_glyphs,
175                                          shaper_item->item.bidiLevel % 2);
176
177        HB_FREE_STACKARRAY(shapedChars);
178
179        if (!haveGlyphs)
180            return FALSE;
181
182        HB_HeuristicPosition(shaper_item);
183    }
184
185    return TRUE;
186}
187
188