/** * Yudit Unicode Editor Source File * * GNU Copyright (C) 1997-2006 Gaspar Sinai * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, * dated June 1991. See file COPYYING for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "stoolkit/SGlyph.h" #include "stoolkit/SParagraph.h" #include "stoolkit/SBinHashtable.h" #include "stoolkit/SUtil.h" #include "stoolkit/STextData.h" #include "stoolkit/SCluster.h" typedef enum { SD_SH_ISOLATED=0, SD_SH_INITIAL=1, SD_SH_MEDIAL=2, SD_SH_FINAL=3, /* Syriac U+0710 extra shapes */ SD_SH_SYRIAC_ALAPH_FINAL_FJ=4, SD_SH_SYRIAC_ALAPH_ISOLATED_FN=5, SD_SH_SYRIAC_ALAPH_ISOLATED_FX=6, SD_BENGALI_INITIAL=7, SD_SH_NOSHAPE=SD_NOSHAPE } SShape; SBinHashtable glyphCache; static char getSimpleShape (const SS_UCS4* now, const SS_UCS4* before, const SS_UCS4* after); /*------------------------------------------------------------------------------ * The class SFallbackShapes should not be in this file. *------------------------------------------------------------------------------ * This is used to chache shapes internally. * TODO: move this to another file. */ class SFallbackShapes { public: SFallbackShapes(); ~SFallbackShapes(); const SS_UCS4* get (unsigned int shape, const SS_UCS4* ochars, unsigned int sizes); void put (unsigned int shape, const SS_UCS4* ochars, const SS_UCS4* schars, unsigned int sizes); private: SBinHashtable shape0; SBinHashtable shape1; SBinHashtable shape2; SBinHashtable shape3; }; SFallbackShapes::SFallbackShapes() { } SFallbackShapes::~SFallbackShapes() { /* FIXME: cleanup allocated SS_UCS4* arrays */ } const SS_UCS4* SFallbackShapes::get (unsigned int shape, const SS_UCS4* ochars, unsigned int sizes) { const SS_UCS4* elem = 0; SString skey ((char*)ochars, sizes * sizeof (SS_UCS4)); switch (shape) { case 0: elem = shape0.get (skey); break; case 1: elem = shape1.get (skey); break; case 2: elem = shape2.get (skey); break; case 3: elem = shape3.get (skey); break; } /* referecing live data */ return elem; } void SFallbackShapes::put (unsigned int shape, const SS_UCS4* ochars, const SS_UCS4* schars, unsigned int sizes) { SS_UCS4* vp = new SS_UCS4[sizes]; CHECK_NEW (vp); SString skey ((char*)ochars, sizes * sizeof (SS_UCS4)); /* copy over the shaped chars. */ for (unsigned int i=0; ishaped ? 0 : SD_NOSHAPE; /* first or not shaped */ } /** * This is the definition of one glyph. It may be composed of * several characters. * @param decomp is the character array in order. It ALWAYS contains chars. * @param comp is the composition character. It might be 0. In this * case decomp needs to be used. * @param shaped is used only if shared is not chached. It tells if * the glyph is shaped. * @param cluster - points to the cluster boundary * @param compindex - points to the extra composing boundary * YOU SOULD ALSO SET usePrecomp aftwerwards. * * ONLY PRECOMPOSED OR SINGLE CHARS WILL BE CACHED! * comp is nonzero for precomposed chars. */ SGlyph::SGlyph (const SV_UCS4 &decomp, SS_UCS4 comp, bool shaped, unsigned int cluster, unsigned int compindex) { selected = false; underlined = false; usePrecomp = true; embedding = false; SS_UCS4 precomp = comp; /* There is no such thing as a one character composition */ if (decomp.size()==1) precomp = decomp[0]; SString skey = (precomp==0 || compindex>0) ? SString() : SString((char*)&precomp, sizeof (SS_UCS4)); if (skey.size()==0 && decomp.size() > 1) { skey = SString ((char*) decomp.array(), sizeof (SS_UCS4) * decomp.size()); } SGlyphShared* shr = 0; if ((shr=glyphCache.get (skey))) { shared = shr; } else { shared = new SGlyphShared(); shared->shaped = shaped; shared->cluster = cluster; shared->composing = compindex; /* * We save only * 1. chars with precomposition * 2. single chars */ shared->precomposed = precomp; /* Memory is expensive, don't waste on single chars */ if (decomp.size() > 1 || compindex > 0) { shared->ucs4v = decomp; } if (skey.size()) { glyphCache.put (skey, shared); } SS_UCS4 fchar = getFirstChar(); shared->type = (char) getCharClass(fchar); shared->bidi = (char) getBiDiClass(fchar); shared->mirror = precomp ? getMirroredCharacter(precomp) : 0; shared->tab = (fchar == SD_CD_TAB); shared->lineend = (fchar==SD_CD_LF || fchar==SD_CD_CR || fchar==SD_CD_PS || fchar==SD_CD_LS); /* clusters and stuff need shared->type and shared->bidi */ if (cluster>0) { /* decompose */ SS_UCS4 firstchar = decomp[cluster]; shared->type = (char) getCharClass(firstchar); shared->bidi = (char) getBiDiClass(firstchar); } } currentShape = shared->shaped ? 0 : SD_NOSHAPE; /* first or not shaped */ } /** * Modify this glyph by adding extra composing characters * to it. * @param c is a new composing character to be added. * @return true if this was a composing character. */ bool SGlyph::addComposing(SS_UCS4 c) { //fprintf (stderr, "1\n"); if (c==0) return false; if (isEOP()) return false; SV_UCS4 chars = getChars(); chars.append (c); unsigned int to = 0; SParagraph pg(chars, &to); if (pg.size()!=1) return false; if (to != chars.size()) return false; shared = (SGlyphShared*) pg[0].shared; usePrecomp = pg[0].usePrecomp; return true; } /** * @return the removed composing character if any * return 0 if there are no more composing characters. */ SS_UCS4 SGlyph::removeComposing() { SV_UCS4 chars = getChars(); if (chars.size()<2) return 0; SS_UCS4 c = chars[chars.size()-1]; chars.truncate (chars.size()-1); unsigned int to = 0; SParagraph pg(chars, &to); if (pg.size()!=1) return 0; if (to != chars.size()) return 0; shared = (SGlyphShared*) pg[0].shared; usePrecomp = pg[0].usePrecomp; return c; } /*------------------------------------------------------------------------------ * SGlyph Work on characters *------------------------------------------------------------------------------ */ /** * Get the composition of the glyph. If this is a shaped * Glyph this is the ORIGINAL composition - if any. */ const SS_UCS4 SGlyph::getChar() const { return shared->precomposed; } /* * Get the mirrored version of the glyph. */ const SS_UCS4 SGlyph::getMirroredChar() const { return shared->mirror; } /** * Get the current shape of a shaped glyph. * If glyph is not shaping return getChar. */ const SS_UCS4 SGlyph::getShapedChar() const { if (shared->shaped && currentShape != SD_NOSHAPE) { /* hard-coded 3 extra shapes for SYRIAC_ALAPH U+0710 */ if ((unsigned int)currentShape == (unsigned int)SD_SH_SYRIAC_ALAPH_FINAL_FJ) { return (SS_UCS4) 0xA0005710; } if ((unsigned int)currentShape == (unsigned int)SD_SH_SYRIAC_ALAPH_ISOLATED_FN) { return (SS_UCS4) 0xA0006710; } if ((unsigned int)currentShape == (unsigned int)SD_SH_SYRIAC_ALAPH_ISOLATED_FX) { return (SS_UCS4) 0xA0007710; } SS_UCS4 curr = shared->ucs4v.array()[(unsigned int)currentShape]; if (curr) return curr; /* some fallback. if we are lucky... */ } if ((unsigned int)currentShape == (unsigned int)SD_BENGALI_INITIAL) { unsigned int en = (SD_BENGALI_BEGIN << 16) | 0x80000000; return ( (shared->precomposed & 0xffff) | en); } return shared->precomposed; } /** * Get the first character of this composition or decomposition */ const SS_UCS4 SGlyph::getFirstChar () const { SS_UCS4 g = getChar(); if (g==0 && (compSize() > 0 || decompSize() > 0)) { g = getDecompArray()[0]; } return g; } /** * In certain cases - when the font does not contain the * shaped character and we need to fall back to the original * characters. Original characters are unshaped - this * routine is making them shaped. * This is hard coded for now. * @return null if there are fallback shapes */ const SS_UCS4* SGlyph::getShapeFallback() const { if (!shared->shaped || fallbackShapes == 0 || (unsigned int)currentShape > 3) return 0; unsigned int dcsize = decompSize(); if (dcsize==0) return 0; return fallbackShapes->get ((unsigned int)currentShape, getDecompArray(), dcsize); } /*------------------------------------------------------------------------------ * SGlyph Work on decompositions *------------------------------------------------------------------------------ */ /** * Get the precomposed characters _or_ the composition characters * add extra composing characters at the end. * If precomposed characters we input, return them, if decomposed * were input return them. It returns the unicode representation * of the given cluster. All extra composing character are also * added. */ SV_UCS4 SGlyph::getChars() const { SV_UCS4 ret; /* If precomposed char should be used or there are no decomps */ if ((usePrecomp && getChar() != 0) || decompSize()==0) { ret.append (getChar()); /* add extra composing stuff, if any */ if (shared->composing > 0) { unsigned int sz = shared->ucs4v.size(); for (unsigned int i=(unsigned int)shared->composing; iucs4v[i]); } } return SV_UCS4(ret); } /* TODO: shaping on clusters */ if (shared->cluster!=0) { /* just add the composing also which is at the end of the array */ unsigned int sz = shared->ucs4v.size(); for (unsigned int i=(unsigned int)shared->cluster; iucs4v[i]); } return SV_UCS4(ret); } /* just add the composing also which is at the end of the array */ unsigned int sz = shared->ucs4v.size(); unsigned int from = (shared->shaped)?4:0; for (unsigned int i=from; iucs4v.array()[i]); } return SV_UCS4(ret); } /** * @return the size of the decomposition buffer */ unsigned int SGlyph::decompSize() const { if (shared->shaped) { /* For shaped we have an offset */ if ((unsigned int)shared->composing ==0)return shared->ucs4v.size()-4; return (unsigned int)shared->composing - 4; } if (shared->cluster!=0) { return(unsigned int) shared->cluster; } if ((unsigned int)shared->composing==0) return shared->ucs4v.size(); return (unsigned int)shared->composing; } /** * @return the size of the extra composition buffer */ unsigned int SGlyph::compSize() const { if (shared->composing == 0) return 0; return shared->ucs4v.size() - (unsigned int) shared->composing; } /** * Return the decomposition array of the glyph. * If the glyph is a shaped glyph it still returns the * correct decomposition - if any. */ const SS_UCS4* SGlyph::getDecompArray() const { if (shared->shaped) { /* For shaped we have an offset */ return &shared->ucs4v.array()[4]; } return shared->ucs4v.array(); } /** * Return the omposition array of the glyph. * If the glyph is a shaped glyph it still returns the * correct composition - if any. * The compositon has to be applied to the whole rendered cluster. */ const SS_UCS4* SGlyph::getCompArray() const { return &shared->ucs4v.array()[(unsigned int) shared->composing]; } /** * Return the shape array of the glyph. * If the glyph is not shaped returned 0 * the array has a size of foru and it contains the * isolated, initial, medial, final forms * If a form iz 0 it is not defined. */ const SS_UCS4* SGlyph::getShapeArray() const { if (shared->shaped) { /* For shaped we have an offset */ return shared->ucs4v.array(); } return 0; } /** * return the decomposed character or shaped array, at a certain place. */ SS_UCS4 SGlyph::operator[] (unsigned int index) const { if (shared->shaped) { /* For shaped we have an offset */ return shared->ucs4v.array()[4 + index]; } return shared->ucs4v.array()[index]; } /* * check if the character has to be mirrored in RTL */ bool SGlyph::isMirrorable() const { return (shared->mirror != 0); } /** * check if this character is special - lineend, tab LRM RLM * These require special rendering. */ bool SGlyph::isSpecial() const { SS_UCS4 fc = getFirstChar(); return (shared->lineend || shared->tab || fc < 0x20 || fc == SD_CD_LRM || fc == SD_CD_RLM || fc==SD_CD_ZWJ || fc==SD_CD_ZWNJ); } /* * Get character type. */ SD_CharClass SGlyph::getType() const { return (SD_CharClass) ((unsigned char)shared->type); } /* * Get character type. */ SD_BiDiClass SGlyph::getBiDiType() const { return (SD_BiDiClass) ((unsigned char)shared->bidi); } /** * Check if this is a white space. */ bool SGlyph::isWhiteSpace() const { if (isSpecial ()) return true; switch (getFirstChar()) { case 0x20: case 0x1680: case 0x2000: case 0x2001: case 0x2002: case 0x2003: case 0x2004: case 0x2005: case 0x2006: case 0x2007: case 0x2008: case 0x2009: case 0x200a: // case 0x2028: LINE SEPARATOR case 0x202f: case 0x205f: case 0x3000: return true; } return false; } /** * Check if this is a valid number * Supports many of the number in unicode, including CJK, arabic, hebrew and greek... * Addition by Maarten van Gompel */ bool SGlyph::isNumber() const { if (isSpecial ()) return true; SD_CharClass type = getType(); switch (type) { case SD_CC_Nd: case SD_CC_Nl: case SD_CC_No: return true; default: break; } return false; } /** * Check if this is a valid delimiter * Supports many of the delimiters in unicode, including CJK delimiters, arabic, hebrew and greek... * Addition by Maarten van Gompel */ bool SGlyph::isDelimiter() const { if (isSpecial ()) return true; SD_CharClass type = getType(); switch (type) { case SD_CC_Pc: case SD_CC_Pd: case SD_CC_Ps: case SD_CC_Pe: case SD_CC_Pi: case SD_CC_Pf: case SD_CC_Po: case SD_CC_Zs: case SD_CC_Zl: case SD_CC_Zp: return true; default: break; } return false; } /** * Check if this is a transparent character. */ bool SGlyph::isTransparent() const { // From Miikka-Markus Alhonen: // "T = Mn + Cf - ZWNJ - ZWJ" (ArabicShaping-4.txt of UCD 3.1.1) // This means that every character belonging to character classes // Mn (04) or Cf (0E) except for ZWNJ U+200C and ZWJ U+200D - and // nothing else - is transparent. // So, even all the characters in Combining Diacritical Marks // U+0300 - U+036F, Hebrew vowels U+0591 - U+05BD, Syriac vowels // U+0730 - U+074A etc. are transparent characters, not just the // Arabic tashkeel //return (gcategory.encode (u4) == 0x4 && ...); // Mark, Non-Spacing if (shared->type == (char)SD_CC_Mn || (char)shared->type == SD_CC_Me || shared->type == (char)SD_CC_Cf) { SS_UCS4 u4 = getFirstChar(); return (u4 != SD_CD_ZWNJ && u4 != SD_CD_ZWJ); } return false; } /** * return true if this is the end of a paragraph */ bool SGlyph::isEOP() const { return (shared->lineend && shared->precomposed != SD_CD_LS); } /** * return true if this is the end of a paragraph. */ bool SGlyph::isEOL() const { return (shared->precomposed == SD_CD_LS); } bool SGlyph::isTab() const { return shared->tab; } bool SGlyph::isCluster() const { return shared->cluster != 0; } /** * We use private area to define our own ligatures. */ bool SGlyph::isYuditLigature() const { if (decompSize()==0) return 0; /* We might above ligature for shaping */ SS_UCS4 ch = getShapedChar(); return isLigature (ch); } /** * We use private area to define our own precomposed characters. */ bool SGlyph::isYuditComposition() const { if (decompSize()==0) return 0; /* We might above ligature for shaping */ SS_UCS4 ch = getShapedChar(); if (ch>= 0xA0000000) { return true; } return false; } /** * Construct a key from characters inside the glyph * @return the key. the key does not contain attributes, * it only has glyph info. */ SString SGlyph::charKey() const { if (decompSize()+compSize() == 0) { SS_UCS4 chr = getChar(); return SString ((char*) &chr, sizeof (SS_UCS4)); } return SString ((char*) shared->ucs4v.array(), shared->ucs4v.size() * sizeof (SS_UCS4)); } /*------------------------------------------------------------------------------ * comparison *------------------------------------------------------------------------------ */ bool SGlyph::operator == (const SGlyph& g2) const { if (shared->ucs4v.size() != g2.shared->ucs4v.size()) { return false; } if (shared->ucs4v.size()==0) { return (getChar() == g2.getChar()); } for (unsigned int i=0; iucs4v.size(); i++) { if (shared->ucs4v[i] != g2.shared->ucs4v[i]) return false; } return true; } bool SGlyph::operator != (const SGlyph& g2) const { if (shared->ucs4v.size() != g2.shared->ucs4v.size()) { return true; } if (shared->ucs4v.size()==0) { return (getChar() != g2.getChar()); } for (unsigned int i=0; iucs4v.size(); i++) { if (shared->ucs4v[i] != g2.shared->ucs4v[i]) return true; } return false; } /** * Cache fallback shapes into * fbIsolated, fbInitial, fbMedial, fbFinal global hashTables. * @param shapes tells us what shapes will be present. * this is an array of size 4, (isolated, initial, medial, final) * @param chars tells us the characters we need shapes for. * @param size is the size of chars array. */ void addFallbackShapes (SUniMap* shaper, const SS_UCS4* shapes, const SS_UCS4* chars, unsigned int csize) { if (csize ==0) return; /* roboustness */ /* first build shape arrays */ SV_UCS4 isol; SV_UCS4 init; SV_UCS4 medi; SV_UCS4 fina; if (fallbackShapes == 0) { fallbackShapes = new SFallbackShapes(); CHECK_NEW (fallbackShapes); } /* isolated will aways have it */ if (fallbackShapes->get (0, chars, csize)) { return; } unsigned int i; for (i=0; ilift (v, 0, false, &ret); /* the composition comes at the end - if any */ if (n==1 && ret.size()==4) { isol.append (ret[0]); init.append (ret[1]); medi.append (ret[2]); fina.append (ret[3]); } /* make it transparent: deal with SD_CD_ZWJ for now. */ else if (chars[i]== SD_CD_ZWJ || chars[i]== SD_CD_ZWNJ) { isol.append (chars[i]); init.append (chars[i]); medi.append (chars[i]); fina.append (chars[i]); } else /* no shapes - treat as isol */ { isol.append (chars[i]); init.append (0); medi.append (0); fina.append (0); } } /* now collect shape arrays */ SV_UCS4 isolA; SV_UCS4 initA; SV_UCS4 mediA; SV_UCS4 finaA; for (i=0; i=0 && (chars[(unsigned int)i0] == SD_CD_ZWJ || chars[(unsigned int)i0] == SD_CD_ZWNJ)) i0--; /* Emulate previous */ if (i0>=0) { prev[0] = isol[(unsigned int)i0]; prev[1] = init[(unsigned int)i0]; prev[2] = medi[(unsigned int)i0]; prev[3] = fina[(unsigned int)i0]; } else if (j==(unsigned int)SD_SH_MEDIAL || j==(unsigned int)SD_SH_FINAL) { /* Previous had initial or medial */ prev[0] = 0; prev[1] = 1; prev[2] = 1; prev[3] = 0; } else /* ISOLATED INITIAL */ { /* Previous has isolated or final */ prev[0] = 1; prev[1] = 0; prev[2] = 0; prev[3] = 1; } /* there is next */ unsigned int i2 = i+1; /* skip ZWJ and ZWNJ */ while (i2put (0, chars, isolA.array(), csize); if (initA.size()==csize) fallbackShapes->put (1, chars, initA.array(), csize); if (mediA.size()==csize) fallbackShapes->put (2, chars, mediA.array(), csize); if (finaA.size()==csize) fallbackShapes->put (3, chars, finaA.array(), csize); } /** * calcualte the current shape * return true if shape changed */ bool SGlyph::setShape(const SGlyph* gbefore, const SGlyph* gafter) { if (!shared->shaped && getLigatureScriptCode (shared->precomposed)!=SD_BENGALI) { return false; } char shape = getShape (gbefore, gafter); if (shape == currentShape) return false; currentShape = shape; return true; } /** * Get the shape at the current position * Please note that it works in visual order! * @return *
    *
  • SD_NOSHAPE no shape
  • *
  • 0 isolated
  • *
  • 1 initial (space after-rl)
  • *
  • 2 medial
  • *
  • 3 final (space before-rl)
  • *
* @param gbefore is the glyph before this line, transparent chars skipped * @param gafter is the glyph before this line, transparent chars skipped */ char SGlyph::getShape(const SGlyph* gbefore, const SGlyph* gafter) { static SS_UCS4 initials[4] = {0x0, 0x0, 0x0, 0x0}; static SS_UCS4 dualjoining[4] = {1, 1, 1, 1}; /* is it a shapeable one ? */ const SS_UCS4* now = getShapeArray(); if (now == 0) { if (getLigatureScriptCode (shared->precomposed)==SD_BENGALI) { if (gbefore==0 || (getLigatureScriptCode (gbefore->shared->precomposed)!=SD_BENGALI && getUnicodeScript (gbefore->getFirstChar()) != SD_BENGALI)) { return (char) SD_BENGALI_INITIAL; } } return (char) SD_SH_NOSHAPE; } const SS_UCS4* before = initials; const SS_UCS4* after = initials; if (gbefore) { before=gbefore->getShapeArray(); if (before==0) before=initials; /* tatweel and ZWJ are dual joining */ if (gbefore->getFirstChar() == SD_CD_ARABIC_TATWEEL || gbefore->getFirstChar() == SD_CD_ZWJ) before = dualjoining; } if (gafter) { after=gafter->getShapeArray(); if (after==0) after=initials; /* tatweel and ZWJ are dual joining */ if (gafter->getFirstChar() == SD_CD_ARABIC_TATWEEL || gafter->getFirstChar() == SD_CD_ZWJ) after = dualjoining; } /* Make it all rl for simplicity */ SS_UCS4 beforeChar = (gbefore==0) ? 0 : gbefore->getFirstChar(); SS_UCS4 afterChar = (gafter==0) ? 0 : gafter->getFirstChar(); bool syriacEOWAlaph = false; syriacEOWAlaph = (afterChar < 0x070f || afterChar > 0x074f) && (afterChar < 0x0621 || afterChar > 0x065f) && afterChar != SD_CD_ZWJ; syriacEOWAlaph = syriacEOWAlaph && (getFirstChar() == 0x0710); /* This is not End of word. Isolated. */ if (syriacEOWAlaph && (beforeChar < 0x070f || beforeChar > 0x074f) && beforeChar != SD_CD_ARABIC_TATWEEL && beforeChar != SD_CD_ZWJ) { return (char) SD_SH_ISOLATED; } /* End-Of-Word rules for Syriac Alaph */ if (syriacEOWAlaph) { /* FX - Isolated end of the word when preceded by Syriac dalath or rish: U+0715 U+0716 U+072A */ if (beforeChar == SD_CD_SYRIAC_LETTER_DALATH || beforeChar == SD_CD_SYRIAC_LETTER_DOTLESS_DALATH || beforeChar == SD_CD_SYRIAC_LETTER_RISH) { return (char) SD_SH_SYRIAC_ALAPH_ISOLATED_FX; } /* FJ - Final end of word */ if (before[(unsigned int)SD_SH_INITIAL]) { return (char) SD_SH_SYRIAC_ALAPH_FINAL_FJ; } /* FN - Isolated end of word except when preceded by Syriac dalath or rish */ return (char) SD_SH_SYRIAC_ALAPH_ISOLATED_FN; } /* call shaper */ return getSimpleShape (now, before, after); } /** * This simple shaper is used after * exceptions are applied, and when a fallback shape * is generated * @param now is the current shape array * @param before is the shape array of the previous character * @param after is the shape array of the next character * before and after is in logical order. * The shape array contains 4 elements for * isolated, initial, medial and final forms. */ static char getSimpleShape (const SS_UCS4* now, const SS_UCS4* before, const SS_UCS4* after) { if ((before[(unsigned int)SD_SH_INITIAL]) && now[(unsigned int)SD_SH_MEDIAL] && (after[(unsigned int)SD_SH_FINAL])) { return (char) SD_SH_MEDIAL; } if (after[(unsigned int)SD_SH_FINAL] && now[(unsigned int)SD_SH_INITIAL]) { return (char) SD_SH_INITIAL; } if (before[(unsigned int)SD_SH_INITIAL] && now[(unsigned int)SD_SH_FINAL]) { return (char) SD_SH_FINAL; } if (now[(unsigned int)SD_SH_ISOLATED]) { return (char) SD_SH_ISOLATED; } /* fallback */ return (char) SD_SH_NOSHAPE; } SGlyphShared* getGlyphShared (SS_UCS4 c) { return glyphCache.get (SString((char*)&c, sizeof (SS_UCS4))); }