Home | History | Annotate | Download | only in src
      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
     34 static 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 */
     45 HB_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