"Fossies" - the Fresh Open Source Software Archive

Member "yudit-3.0.7/swindow/sx11/SX11Font.cpp" (7 Jun 2020, 26232 Bytes) of package /linux/misc/yudit-3.0.7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "SX11Font.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.0.5_vs_3.0.7.

    1 /** 
    2  *  Yudit Unicode Editor Source File
    3  *
    4  *  GNU Copyright (C) 1997-2006  Gaspar Sinai <gaspar@yudit.org>  
    5  *
    6  *  This program is free software; you can redistribute it and/or modify
    7  *  it under the terms of the GNU General Public License, version 2,
    8  *  dated June 1991. See file COPYYING for details.
    9  *
   10  *  This program is distributed in the hope that it will be useful,
   11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  *  GNU General Public License for more details.
   14  *
   15  *  You should have received a copy of the GNU General Public License
   16  *  along with this program; if not, write to the Free Software
   17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   18  */
   19  
   20 #include "swindow/sx11/SX11Font.h"
   21 #include "stoolkit/SBinHashtable.h"
   22 #include "stoolkit/SExcept.h"
   23 #include "stoolkit/SUtil.h"
   24 #include "stoolkit/SCluster.h"
   25 #include <X11/Xatom.h>
   26 
   27 #include <stdio.h>
   28 
   29 #define SD_ZW_FOTNTHACK 0.0001
   30 
   31 static SStringVector mapNameVector;
   32 /**
   33  * Maps SCluster.h SD_DEVANAGARI - like codes to pango codes.
   34  */
   35 static SStringVector scriptCodeMap("*-iso10646-yudit,*-iso10646-dev,*-iso10646-bng,*-iso10646-gur,*-iso10646-guj,*-iso10646-ori,*-iso10646-tam,*-iso10646-tel,*-iso10646-kan,*-iso10646-mal,*-iso10646-sin");
   36 
   37 
   38 /**
   39  * forcefully sets coverage even if glyph is zero size
   40  */
   41 
   42 static bool inited= false;
   43 static const char* defaultNames[] =
   44 {
   45 "*-iso10646-1", "ucs2",
   46 "*-iso10646-dev", "ucs2",
   47 "*-iso10646-tam", "ucs2",
   48 "*-iso10646-bgn", "ucs2",
   49 "*-iso10646-bng", "ucs2",
   50 "*-iso10646-bur", "ucs2",
   51 "*-iso10646-gur", "ucs2",
   52 "*-unicode", "ucs2",
   53 "*-iso8859-1", "iso-8859-1",
   54 "*-iso8859-2", "iso-8859-2",
   55 "*-iso8859-3", "iso-8859-3",
   56 "*-iso8859-4", "iso-8859-4",
   57 "*-iso8859-5", "iso-8859-5",
   58 "*-iso8859-6", "iso-8859-6",
   59 "*-iso8859-7", "iso-8859-7",
   60 "*-iso8859-8", "iso-8859-8",
   61 "*-iso8859-9", "iso-8859-9",
   62 "*-iso8859-11", "iso-8859-11",
   63 "*-iso8859-12", "iso-8859-12",
   64 "*-iso8859-13", "iso-8859-13",
   65 "*-iso8859-14", "iso-8859-14",
   66 "*-iso8859-15", "iso-8859-15",
   67 "*-iso8859-16", "iso-8859-16",
   68 "*-koi8-r", "koi8-r",
   69 "*-koi8-c", "koi8-c",
   70 "*-koi8-u", "koi8-u",
   71 "*-koi8-1", "koi8-r",
   72 
   73 "*-ksc5601.1987-0", "ksc-5601-l",
   74 "*-ksc5601*", "ksc-5601-l",
   75 
   76 "*-ksx1001*", "ksx-1001",
   77 
   78 "*-jisx0208.1983-0", "jis-0208",
   79 "*-jisx0208*", "jis-0208",
   80 
   81 "*-jisx0208.1990-0", "jis-0208",
   82 "*-jisx0208*", "jis-0208",
   83 
   84 "*-jisx0201.1976-0", "jis-0201",
   85 "*-jisx0201*", "jis-0201",
   86 
   87 "*-jisx0212.1990-0", "jis-0212",
   88 "*-jisx0212*-0", "jis-0212",
   89 
   90 "*-jisx0213*-1", "jis-0213-1",
   91 "*-jisx0213*-2", "jis-0213-2",
   92 
   93 "*-big5.hku-0", "big-5",
   94 "*-big5-0", "big-5",            
   95 "*-big5*", "big-5",            
   96 
   97 "*-gb2312.1980-0", "gb-2312-l", /* There are some font encoded with gb-2312-r"*/
   98 "*-gb2312*", "gb-2312-l", /* There are some font encoded with gb-2312-r"*/
   99 
  100 "*-iso646*", "iso-646", /* 646 is not 10646 */
  101 };
  102 
  103 /**
  104  * @author: Gaspar Sinai <gaspar@yudit.org>
  105  * @version: 2000-06-03
  106  * This is a specific native font, with a certain size
  107  */
  108 class SX11FontCache
  109 {
  110 public:
  111   SX11FontCache (SX11Impl *impl,const SString& xlfd);
  112   ~SX11FontCache ();
  113   SString xlfd;
  114   SX11Impl* impl;
  115   SUniMap map;
  116   double fontAscent;
  117   double fontDescent;
  118   double fontWidth;
  119   XFontStruct* font;
  120   bool loaded;
  121   bool load ();
  122   unsigned int getRawPangoLigatures (unsigned int script, SS_UCS4* ino, 
  123        unsigned int len);
  124   unsigned int getPangoLigatures (unsigned int script, SS_UCS4* ino, 
  125        unsigned int len);
  126 
  127   double draw (SCanvas* canvas, const SPen& pen, double x, double y, 
  128        SS_UCS2 glyphno);
  129   double width (SS_UCS2 glyphno);
  130 
  131   SProperties ligatures;
  132   unsigned int ligatureCount;
  133   int scriptCode;
  134   /* Still no agreement on this - I am using : */ 
  135   /* http://mail.nl.linux.org/linux-utf8/2000-12/msg00042.html */
  136   unsigned int plane;
  137 };
  138 
  139 
  140 SX11FontCache::SX11FontCache(SX11Impl *_impl,  const SString& _xlfd) 
  141      : xlfd (_xlfd)
  142 {
  143   impl = _impl;
  144   fontAscent = 0.0;
  145   fontDescent = 0.0;
  146   fontWidth = 0.0;
  147   loaded = false;
  148   plane = 0;
  149 }
  150 
  151 /**
  152  * Create an XLFD and try to load this as a font.
  153  * TODO: it should modify xlfd to show the real xlfd
  154  */
  155 bool
  156 SX11FontCache::load ()
  157 {
  158   if (loaded) return (font!=0);
  159   scriptCode = -1;
  160   plane = 0;
  161   loaded = true;
  162   char* fontName = xlfd.cString();
  163   font = XLoadQueryFont (impl->display, fontName);
  164   delete fontName;
  165   if (font == 0)  return false;
  166   /* Fix broken fonts */
  167   Atom fontatom = XInternAtom (impl->display, "FONT", False);
  168   Atom pangohack = XInternAtom (impl->display, "PANGO_LIGATURE_HACK", False);
  169   char* vle = 0;
  170   char* pangohackname = 0;
  171   XFontProp *fp = font->properties;
  172   for (int i = 0; i < font->n_properties; i++, fp++)
  173   {
  174     if (fp->name == fontatom)
  175     {
  176       vle = XGetAtomName (impl->display, fp->card32);
  177     }
  178     else if (fp->name == pangohack)
  179     {
  180       pangohackname = XGetAtomName (impl->display, fp->card32);
  181     }
  182   }
  183   if (vle)
  184   {
  185     SString fn(vle);
  186     XFree (vle);
  187     fn.lower();
  188     xlfd = fn;
  189   }
  190 
  191   int tryAscent[2];
  192   tryAscent[0] = font->ascent;
  193   tryAscent[1] = font->max_bounds.ascent;
  194   if (tryAscent[1] < tryAscent[0]
  195      || (tryAscent[0] +2) * 2 < tryAscent[1])
  196   {
  197     tryAscent[1] = tryAscent[0];
  198   }
  199   int tryDescent[2];
  200   tryDescent[0] = font->descent;
  201   tryDescent[1] = font->max_bounds.descent;
  202   if (tryDescent[1] < tryDescent[0]
  203      || (tryDescent[0] +2) * 2 < tryDescent[1])
  204   {
  205     tryDescent[1] = tryDescent[0];
  206   }
  207   fontAscent = (double) tryAscent[1];
  208   fontDescent = (double)tryDescent[1];
  209   fontWidth = (double) font->max_bounds.width;
  210   if (!inited)
  211   {
  212     for (unsigned int i=0; i<sizeof (defaultNames) / sizeof (char*)/2; i++)
  213     {
  214       mapNameVector.append (defaultNames[2*i]);
  215       mapNameVector.append (defaultNames[2*i+1]);
  216     }
  217     inited = true;
  218   }
  219   /* Find out what map is used for this font */
  220   SString m;
  221   unsigned int i;
  222   for (i=0; i<scriptCodeMap.size(); i++)
  223   {
  224      if (xlfd.match (scriptCodeMap[i]))
  225      {
  226         scriptCode = (int)i;
  227      }
  228   }
  229   for (i=0; i+1<mapNameVector.size(); i=i+2)
  230   {
  231     if (xlfd.match (mapNameVector[i]))
  232     {
  233       m = mapNameVector[i+1];
  234       break;
  235     }
  236   }
  237   for (i=1; i<17; i++)
  238   {
  239     /* http://mail.nl.linux.org/linux-utf8/2000-12/msg00024.html */
  240     SString s("*-iso10646p2-");
  241     s.print (i);
  242     if (xlfd.match (s))
  243     {
  244       plane = i;
  245       if ((m.size() ==0)) m=SString("ucs2");
  246       break;
  247     }
  248 /* Hmm.
  249     SString s2("*-iso10646.2-");
  250     s2.print (i);
  251     if (xlfd.match (s2))
  252     {
  253       plane = i;
  254       if ((m.size() ==0)) m=SString("ucs2");
  255       break;
  256     }
  257 */
  258   }
  259   if (m.size() ==0)
  260   {
  261     fprintf (stderr, "swindow/sx11/SX11Font need fontmap for %*.*s\n",
  262         SSARGS(xlfd));
  263   }
  264   if (m.size() !=0 && m != SString("ucs2"))
  265   {
  266     SUniMap um(m);
  267     if (!um.isOK())
  268     {
  269      fprintf (stderr, "SX11Font .my map '%*.*s' needed for for %*.*s\n",
  270         SSARGS(m), SSARGS(xlfd));
  271     }
  272     else
  273     {
  274       map = um;
  275     }
  276   }
  277   ligatureCount = 0;
  278   /* We just parse the simplest version of PANGO_LIGATURE_HACK */
  279   if (pangohackname)
  280   {
  281     SStringVector hacks(pangohackname, " ");
  282     SBinVector<Atom> hackatoms;
  283     unsigned int i;
  284     for (i=0; i<hacks.size(); i++)
  285     {
  286        SString hi = hacks[i];
  287        SStringVector vi (hi, ":");
  288        if (vi.size()==0) continue; /* never happens */
  289        /* language grossly ingnored */
  290        SString hname = vi[vi.size()-1];
  291        hname.append ((char)0);
  292        Atom atom = XInternAtom (impl->display, hname.array(), False);
  293        hackatoms.append (atom);
  294     }
  295     XFontProp *fp = font->properties;
  296     for (int j = 0; j < font->n_properties; j++, fp++)
  297     {
  298       for (i=0; i<hackatoms.size(); i++)
  299       {
  300         if (fp->name != hackatoms[i]) continue;
  301         char* hvle = XGetAtomName (impl->display, fp->card32);
  302         SStringVector hvect (hvle, " ");
  303         for (unsigned int k=0; k<hvect.size(); k++)
  304         {
  305           SString ligkvle=hvect[k];
  306           SStringVector vvle (ligkvle, "+=");
  307           if (vvle.size()==1) continue; /* never happens */
  308           SV_UCS4 v;
  309           bool bad = false;
  310           for (unsigned int m=0; m<vvle.size(); m++)
  311           {
  312             unsigned int l;
  313             SString s = vvle[m];
  314             s.append ((char)0);
  315             char dummy[2];
  316             if (sscanf (s.array(), "%x%1s", &l, dummy)!=1)
  317             {
  318               //fprintf (stderr, "BAD X11 PANGO_HACK='%*.*s' at '%s'\n", 
  319               //  SSARGS(ligkvle), s);
  320               bad = true;
  321               break;
  322             }
  323             v.append (l);
  324           }
  325           if (!bad)
  326           {
  327             SS_UCS4 glyphno = v.array()[v.size()-1];
  328             /* scriptCode should be initialized at this point */
  329             double w = width ((SS_UCS2) glyphno);
  330             if (glyphno > 0xffff || w == 0.0)
  331             {
  332               static bool warned = false;
  333               if (!warned) fprintf (stderr, 
  334                 "SX11Font: Deleting non-existent ligatures from %*.*s\n", 
  335                 SSARGS(xlfd));
  336               warned = true;
  337             }
  338             else
  339             {
  340               SString key ((char*)v.array(), sizeof (SS_UCS4) * (v.size()-1));
  341               SString vle ((char*)&v.array()[v.size()-1], sizeof (SS_UCS4));
  342               ligatureCount++;
  343               ligatures.put (key, vle);
  344             }
  345           }
  346         }
  347       }
  348     }
  349   }
  350   if (ligatureCount != 0)
  351   {
  352 #if 0
  353     fprintf (stderr, "loaded %u ligatures from %*.*s.\n", 
  354         ligatureCount, SSARGS(xlfd));
  355 #endif
  356   }
  357   return true;
  358 }
  359 
  360 /**
  361  * Draw a single glyph.
  362  * @return the width
  363  */
  364 double
  365 SX11FontCache::draw (SCanvas* canvas, const SPen& pen, double x, double y, 
  366        SS_UCS2 glyphno)
  367 {
  368   /* ZWJ and ZWNJ */
  369   /* ZWJ and ZWNJ */
  370   if (plane == 0 && scriptCode != -1 
  371         && (glyphno == 0x200d 
  372          || glyphno == 0x200c))
  373   {
  374       return SD_ZW_FOTNTHACK;
  375   }
  376   bool covered = isCoveredScipt ((SS_UCS4) glyphno, scriptCode);
  377   /* pango subscript and superscirpt ra - zero width */
  378   if (plane == 0 && !covered && scriptCode != -1 && (glyphno == 0xc97e || glyphno == 0xc97f))
  379   {
  380     covered = true;
  381   }
  382 
  383   XChar2b     char2B;
  384   char2B.byte1 = glyphno >> 8;
  385   char2B.byte2 = glyphno & 0xff;
  386   double fwidth;
  387 
  388   if (font->per_char==0)
  389   {
  390     int ddirection;
  391     int dascent;
  392     int ddescent;
  393     XCharStruct overall;
  394     if (!XQueryTextExtents16 (impl->display,
  395        font->fid, &char2B, 1, &ddirection, &dascent, &ddescent, &overall))
  396     {
  397       return 0.0;
  398     }
  399     if (overall.width==0 && !covered) return 0.0;
  400     fwidth = (double) overall.width;
  401     if (fwidth==0.0) fwidth = SD_ZW_FOTNTHACK;
  402   }
  403   else
  404   {
  405     if (char2B.byte1 < font->min_byte1 
  406       || char2B.byte2 < font->min_char_or_byte2
  407       || char2B.byte1 > font->max_byte1 
  408       || char2B.byte2 > font->max_char_or_byte2)
  409     {
  410        return 0.0;
  411     }
  412     int cellIndex = (char2B.byte1-font->min_byte1)
  413      * (font->max_char_or_byte2 -font->min_char_or_byte2+1)
  414      + char2B.byte2-font->min_char_or_byte2;
  415     fwidth = (double) font->per_char[cellIndex].width;
  416     if (fwidth == 0.0 && !covered) return 0.0;
  417     if (fwidth==0.0) fwidth = SD_ZW_FOTNTHACK;
  418   }
  419   canvas->bitfont (pen, x, y, (void*) font->fid, (char*) &char2B, 2);
  420   return fwidth; 
  421 }
  422 
  423 /**
  424  * get width of a single glyph.
  425  * @return the width
  426  */
  427 double
  428 SX11FontCache::width (SS_UCS2 glyphno)
  429 {
  430   /* ZWJ and ZWNJ */
  431   if (plane == 0 && scriptCode != -1 && (glyphno == 0x200d || glyphno == 0x200c))
  432   {
  433       return SD_ZW_FOTNTHACK;
  434   }
  435   /* pango subscript and superscirpt ra - zero width */
  436   bool covered = isCoveredScipt ((SS_UCS4) glyphno, scriptCode);
  437   if (plane == 0 && !covered && scriptCode != -1 && (glyphno == 0xc97e || glyphno == 0xc97f))
  438   {
  439     covered = true;
  440   }
  441   XChar2b     char2B;
  442   char2B.byte1 = glyphno >> 8;
  443   char2B.byte2 = glyphno & 0xff;
  444   if (font->per_char==0)
  445   {
  446     int ddirection;
  447     int ddescent;
  448     int dascent;
  449     XCharStruct overall;
  450     if (!XQueryTextExtents16 (impl->display,
  451        font->fid, &char2B, 1, &ddirection, &dascent, &ddescent, &overall))
  452     {
  453       return 0.0;
  454     }
  455     if (!covered) return (double)overall.width;
  456     if (overall.width == 0) return SD_ZW_FOTNTHACK;
  457     return (double) overall.width;
  458   }
  459   if (char2B.byte1 < font->min_byte1 || char2B.byte2 < font->min_char_or_byte2
  460       || char2B.byte1 > font->max_byte1 || char2B.byte2 > font->max_char_or_byte2)
  461   {
  462      return 0.0;
  463   }
  464   int cellIndex = (char2B.byte1-font->min_byte1)
  465      * (font->max_char_or_byte2 -font->min_char_or_byte2+1)
  466      + char2B.byte2-font->min_char_or_byte2;
  467   double ret =  (double) font->per_char[cellIndex].width;
  468   if (!covered) return ret;
  469   if (ret == 0.0) return SD_ZW_FOTNTHACK;
  470   return ret;
  471 }
  472 
  473 SX11FontCache::~SX11FontCache ()
  474 {
  475   if (font) XFreeFont (impl->display, (XFontStruct*) font);
  476 } 
  477 
  478 /**
  479  * TODO: this is never freed up. It simply grows...
  480  */
  481 static SBinHashtable<SX11FontCache*> fontCache;
  482 
  483 static SX11FontCache* loadFont (SX11Impl* impl, const SString& xlfd);
  484 
  485 /**
  486  * Create a new empty font 
  487  * if _encoding exists use it as the decoder for the font.
  488  */
  489 SX11Font::SX11Font (SX11Impl* _impl, const SString& _encoding) 
  490   : encoder (_encoding), impl(_impl)
  491 {
  492   isencoded = (_encoding.size()!=0);
  493   if (isencoded && !encoder.isOK())
  494   {
  495     if (_encoding != "unicode"
  496      && _encoding != "mslvt"
  497      && _encoding != "nojamo"
  498      && _encoding != "indic"
  499      && _encoding != "deva"
  500      && _encoding != "beng"
  501      && _encoding != "guru"
  502      && _encoding != "gujr"
  503      && _encoding != "orya"
  504      && _encoding != "taml"
  505      && _encoding != "telu"
  506      && _encoding != "knda"
  507      && _encoding != "mlym"
  508      && _encoding != "sinh"
  509      && _encoding != "thai"
  510      && _encoding != "lao"
  511      && _encoding != "tibt"
  512      && _encoding != "emoji"
  513     )
  514     {
  515       fprintf (stderr, "SX11Font: unknown encoding : '%*.*s'\n",
  516          SSARGS (_encoding));
  517     }
  518     isencoded = false;
  519   }
  520 }
  521 
  522 SX11Font::~SX11Font ()
  523 {
  524 } 
  525 
  526 /**
  527  * Draw one glyph. 
  528  */
  529 bool
  530 SX11Font::draw (const SString& xlfd, SCanvas* canvas, const SPen& pen, 
  531     const SS_Matrix2D& matrix, SS_UCS4 g)
  532 {
  533   SX11FontCache* fc = loadFont(impl, xlfd);
  534   if (!fc->load()) return false;
  535   /**
  536    * create a glyphno array 
  537    */
  538   SS_UCS2 glyphno = 0;
  539   unsigned int liglen =0;
  540   if (isLigature (g) && fc->ligatureCount != 0 
  541        && (liglen=getLigatureUnicode (g, 0)) > 0)
  542   {
  543      /* we allocate one more to allow for LEFT_RIGHT vowel expansion */
  544      SS_UCS4* chars = new SS_UCS4[liglen + 1];
  545      CHECK_NEW (chars);
  546      getLigatureUnicode (g, chars);
  547      unsigned int script = (g & 0x7fff0000) >> 16;
  548      unsigned int collapsed = fc->getPangoLigatures (script, chars, liglen);
  549      if (collapsed==0 || collapsed == liglen)
  550      {
  551        delete chars;
  552        return false;
  553      }
  554      if (collapsed == 1)
  555      {
  556        glyphno = chars[0];
  557      }
  558      else
  559      {
  560        double x = matrix.t0;
  561        double y = matrix.t1;
  562        /* draw the whole ligature */
  563        for (unsigned int i=0; i<collapsed; i++)
  564        {
  565          double dw = 0.0;
  566          if (chars[i] > 0xffff 
  567              || ((dw = fc->draw (canvas, pen, 
  568                         x, y, (SS_UCS2) chars[i])) == 0.0 && i==0))
  569         {
  570           delete chars;
  571           return false;
  572         }
  573         x += dw;
  574        }
  575        delete chars;
  576        return true;
  577      }
  578      delete chars;
  579   }
  580   else if (isencoded)
  581   {
  582     /* to make things faster */
  583     if (encoder.isUMap())
  584     {
  585       glyphno = encoder.encode (g);
  586     }
  587     else
  588     {
  589       SV_UCS4 ucs4; ucs4.append (g); SV_UCS4 decd;
  590       unsigned int lifted = encoder.lift (ucs4, 0, false, &decd);
  591       /* like TTF we fallback to original font encoding  */
  592       if (lifted == 0)
  593       {
  594          glyphno = fc->map.encode (g);
  595       } 
  596       else
  597       {
  598         double x = matrix.t0;
  599         double y = matrix.t1;
  600         /* draw the whole ligature */
  601         for (unsigned int i=0; i<decd.size(); i++)
  602         {
  603           if (decd[i] > 0xffff) return 0;
  604           double dw = fc->draw (canvas, pen, x, y, (SS_UCS2) decd[i]);
  605           if (dw == 0.0 && i==0)
  606           {
  607             return false;
  608           }
  609           x += dw;
  610         }
  611         return true;
  612       }
  613     }
  614   }
  615   else
  616   {
  617     if (fc->plane == 0)
  618     {
  619       /* faster */
  620       if (fc->map.isUMap()) {
  621         glyphno = fc->map.encode (g);
  622       } else {
  623         SV_UCS4 ucs4; ucs4.append (g); SV_UCS4 decd;
  624         unsigned int lifted = fc->map.lift (ucs4, 0, false, &decd);
  625         if (lifted==1 && decd.size()==1) glyphno = decd[0];
  626       }
  627     }
  628     else
  629     {
  630        SS_UCS4  start = fc->plane*0x10000;;
  631        glyphno =  (g < start) ? 0 : fc->map.encode (g-start);
  632     }
  633   }
  634   if (glyphno==0) return false;
  635   double dw = fc->draw (canvas, pen, matrix.t0, matrix.t1, glyphno);
  636   return dw;
  637 }
  638 
  639 /**
  640  * Get pango ligatures.
  641  * @param ino is the input-output buffer
  642  * @param len is the input length
  643  * @return output length
  644  */
  645 unsigned int
  646 SX11FontCache::getRawPangoLigatures (unsigned int script, 
  647    SS_UCS4* ino, unsigned int len)
  648 {
  649   for (unsigned int i=0; i<len; i++)
  650   {
  651     if (ino[i] > 0xffff) return len;
  652   }
  653   if (len == 1) return 1;
  654   SString l;
  655   /* collect all ligatures in one loop, starting from big ones. */
  656   unsigned int curin=0;
  657   unsigned int curout=0;
  658   unsigned int i;
  659   while (curin < len)
  660   {
  661     bool gotone = false;
  662     for (i=len; i>curin; i--)
  663     {
  664       SString key ((char*)&ino[curin], (i-curin)*sizeof (SS_UCS4));
  665       const SString* lig = ligatures.get (key);
  666       /* get hard-coded pango scripts */
  667       if (lig == 0 && i== curin+2 && scriptCode==(int)script)
  668       {
  669         int type0 = getCharType (ino[curin]);
  670         int type1 = getCharType (ino[curin+1]);
  671         if ((type0 == SD_INDIC_CONSONANT_BASE
  672             || type0 == SD_INDIC_CONSONANT_POST_BASE
  673             || type0 == SD_INDIC_CONSONANT_BELOW_BASE
  674             ) 
  675            && type1 == SD_INDIC_HALANT)
  676         {
  677           /* PANGO intermediate half form */
  678           SS_UCS4 gl = 0;
  679           switch (script)
  680           {
  681           case SD_DEVANAGARI:
  682            gl = 0xf000 + ino[curin];
  683            l = SString ((char*)&gl, sizeof (SS_UCS4));
  684            lig = &l;
  685            break;
  686           default:
  687            break;
  688           }
  689         }
  690         else
  691         {
  692           /* TODO: other stuff */
  693           switch (script)
  694           {
  695           case SD_DEVANAGARI:
  696            break;
  697           default:
  698            break;
  699           }
  700         }
  701       }
  702       if (lig && lig->size() == sizeof (SS_UCS4))
  703       {
  704         memcpy (&ino[curout], lig->array(), sizeof (SS_UCS4));
  705         curout++;
  706         curin = i;
  707         gotone = true;
  708         break;
  709       }
  710     }
  711     if (!gotone)
  712     {
  713       ino[curout] = ino[curin];
  714       curout++;
  715       curin++;
  716     }
  717   }
  718   if (curout == len) return curout;
  719   /* try collapsing further */
  720   return getRawPangoLigatures (script, ino, curout);
  721 }
  722 
  723 /**
  724  * Get pango ligatures, and fix them.
  725  * @param ino is the input-output buffer
  726  * @param len is the input length
  727  * @return output length
  728  */
  729 unsigned int
  730 SX11FontCache::getPangoLigatures (unsigned int script, 
  731    SS_UCS4* ino, unsigned int _len)
  732 {
  733   /* modifiers*/
  734   unsigned int mstart=0;
  735   unsigned int mend=0;
  736   unsigned int i;
  737   unsigned int len = _len;
  738   /* chop off modifier */
  739   for (i=0; i<_len; i++)
  740   {
  741     int type0 = getCharType (ino[i]);
  742     if (i>0 && type0 == SD_INDIC_MODIFIER && mstart==0)
  743     {
  744       mstart = i; mend = len;
  745       len = mstart;
  746     }
  747   }
  748   /* pre-processing */
  749   switch (script)
  750   {
  751   case SD_DEVANAGARI:
  752     if (len > 2 && ino[0] == 0x930 && ino[1] == 0x94d && ino[2] == 0x200d)
  753     {
  754       ino[0] = 0x0931; /* making sure eyelash is eyelash */
  755     }
  756     break;
  757   }
  758   
  759   unsigned int ret = getRawPangoLigatures (script, ino, len);
  760   if (scriptCode != (int)script || ret < 2)
  761   {
  762      /* put back modifiers */
  763      for (i=mstart; i<mend; i++, ret++) ino[ret] = ino[i];
  764      return ret;
  765   }
  766   SS_UCS4 prefix=0;
  767   SS_UCS4 postfix=0;
  768   int endtype = getCharType  (ino[ret-1]);
  769   /* Put stuff in order */
  770   switch (script)
  771   {
  772   case SD_BENGALI:
  773     if (endtype == SD_INDIC_LEFT_VOWEL)
  774     {
  775       prefix = ino[ret-1];
  776     }
  777     else if (endtype == SD_INDIC_LEFT_RIGHT_VOWEL)
  778     {
  779       prefix = getLRVowelLeft (ino[ret-1]);
  780       postfix = getLRVowelRight (ino[ret-1]);
  781     }
  782     /* FIXME: Put pango bengali fuzzy logic here. 
  783                Halant is 0x09CD. Also, there are  left+right vowels  */
  784     /* make room for pre - we have one byte more for this...*/
  785     if (prefix)
  786     {
  787       /* note that we have one more */
  788       for (i=ret-1; i>0; i--)
  789       {
  790         ino[i] = ino[i-1];
  791       }
  792       ino[0] = prefix;
  793       if (postfix)
  794       {
  795         ino[ret] = postfix;
  796         ret++;
  797       }
  798     }
  799     break;
  800   case SD_DEVANAGARI:
  801     if (ino[ret-1] == 0x93f)
  802     {
  803       prefix = 0x93f;
  804     }
  805     /* convert something + RA to subscript ra */
  806     for (i=1; i<ret; i++)
  807     {
  808       if (ino[i-1] >= 0xf900 && ino[i-1] <0xf980 && ino[i] == 0x0930) // RA
  809       {
  810         ino[i-1] = ino[i-1] - 0xf000; /* back to original form */
  811         ino[i] = 0xc97e; /* subscript */
  812       }
  813       if (ino[i-1] >= 0xf900 && ino[i-1] <0xf980 && ino[i] == 0xf000 + 0x0930) // Half RA
  814       {
  815         ino[i-1] = ino[i-1] - 0xf000; /* back to original form */
  816         for (unsigned int j=ret; j>i+1; j--)
  817         {
  818           ino[j] = ino[j-1];
  819         }
  820         ino[i] = 0xc97e; /* subscript */
  821         ino[i+1] = 0x094D; /* halant */
  822         ret++;
  823       }
  824     }
  825     if (ino[0] == 0xf000 + 0x0930)// Starts with half-ra.
  826     {
  827       for (i=1; i<ret; i++)
  828       {
  829         ino[i-1] = ino[i];
  830       }
  831       if (prefix)
  832       {
  833         ino[ret-2] = 0xc97f;
  834       }
  835       else
  836       {
  837         /* we just copied it to ret-2 */
  838         int type0 = getCharType (ino[ret-1]);
  839         switch (type0)
  840         {
  841         case SD_INDIC_RIGHT_VOWEL:
  842         case SD_INDIC_TOP_VOWEL:
  843         case SD_INDIC_BOTTOM_VOWEL:
  844           ino[ret-2] = 0xc97f;
  845           break;
  846         default:
  847           ino[ret-1] = 0xc97f;
  848           break;
  849         }
  850       }
  851     }
  852     /* make room for pre */
  853     if (prefix)
  854     {
  855       for (i=ret-1; i>0; i--)
  856       {
  857         ino[i] = ino[i-1];
  858       }
  859       ino[0] = prefix;
  860     }
  861     /* convert stuff to half forms - everything excpept if it ends with half */
  862     for (i=prefix?1:0; i+1<ret; i++)
  863     {
  864       if (ino[i] >= 0xf000 && ino[i] < 0xf9ff)
  865       {
  866         ino[i] = (ino[i] & 0x0fff) | 0xe000;
  867       }
  868     }
  869     break;
  870   default:
  871     break;
  872   }
  873 #if 0
  874   fprintf (stderr, "Glyphs ");
  875   for (i=0; i<ret; i++)
  876   {
  877     fprintf (stderr, "%04X ", ino[i]);
  878   }
  879   fprintf (stderr, "\n");
  880 #endif
  881   /* put back modifiers */
  882   for (i=mstart; i<mend; i++, ret++) ino[ret] = ino[i];
  883   return ret;
  884 }
  885 
  886 /**
  887  * @return the width of the character at o position.
  888  */
  889 bool
  890 SX11Font::width (const SString& xlfd, SS_UCS4 g, double *width_)
  891 {
  892   if (width_) *width_=0;
  893   SX11FontCache* fc = loadFont(impl, xlfd);
  894   if (!fc->load()) return false;
  895 
  896   SS_UCS2 glyphno = 0;
  897   unsigned int liglen =0;
  898   if (isLigature (g) && fc->ligatureCount != 0 
  899        && (liglen=getLigatureUnicode (g, 0)) > 0)
  900   {
  901      /* we allocate one more to allow for LEFT_RIGHT vowel expansion */
  902      SS_UCS4* chars = new SS_UCS4[liglen + 1];
  903      CHECK_NEW (chars);
  904      getLigatureUnicode (g, chars);
  905      unsigned int script = (g & 0x7fff0000) >> 16;
  906      unsigned int collapsed = fc->getPangoLigatures (script, chars, liglen);
  907      if (collapsed==0 || collapsed == liglen)
  908      {
  909        delete chars;
  910        return false;
  911      }
  912      if (collapsed == 1)
  913      {
  914        glyphno = chars[0];
  915      }
  916      else
  917      {
  918        double x = 0;
  919        /* the whole ligature */
  920        for (unsigned int i=0; i<collapsed; i++)
  921        {
  922          double dw = 0.0;
  923          if (chars[i] > 0xffff 
  924           || ((dw=fc->width ((SS_UCS2) chars[i]))==0.0 && i==0))
  925          {
  926            delete chars;
  927            return false;
  928          }
  929          x += dw;
  930        }
  931        if (width_) *width_ = x;
  932        /* loop */
  933        delete chars;
  934        return true;
  935      }
  936      delete chars;
  937   }
  938   /**
  939    * create a glyphno array 
  940    */
  941   else if (isencoded)
  942   {
  943     /* to make things faster */
  944     if (encoder.isUMap())
  945     {
  946       glyphno = encoder.encode (g);
  947     }
  948     else
  949     {
  950       SV_UCS4 ucs4; ucs4.append (g); SV_UCS4 decd;
  951       unsigned int lifted = encoder.lift (ucs4, 0, false, &decd);
  952       /* Like TTF we fallback to original font encoding  */
  953       if (lifted == 0)
  954       {
  955         glyphno = fc->map.encode (g);
  956       } 
  957       else
  958       {
  959         double x = 0;
  960         /* draw the whole ligature */
  961         for (unsigned int i=0; i<decd.size(); i++)
  962         {
  963           double dw;
  964           if (decd[i] > 0xffff) return false;
  965           if ((dw=fc->width ((SS_UCS2) decd[i]))==0.0 && i==0)
  966           {
  967             return false;
  968           }
  969           x += dw;
  970         }
  971         if (width_) *width_ = x;
  972         return true;
  973       }
  974     }
  975   }
  976   else
  977   {
  978     if (fc->plane == 0)
  979     {
  980       if (fc->map.isUMap()) {
  981         glyphno = fc->map.encode (g);
  982       } else {
  983         SV_UCS4 ucs4; ucs4.append (g); SV_UCS4 decd;
  984         unsigned int lifted = fc->map.lift (ucs4, 0, false, &decd);
  985         if (lifted==1 && decd.size()==1) glyphno = decd[0];
  986       }
  987     }
  988     else
  989     {
  990        SS_UCS4  start = fc->plane*0x10000;;
  991        glyphno =  (g < start) ? 0 : fc->map.encode (g-start);
  992     }
  993   }
  994   if (glyphno==0) return false;
  995   double dw;
  996   if ((dw = fc->width (glyphno))!=0.0)
  997   {
  998     if (width_) *width_ = dw;
  999     return true;
 1000   }
 1001   return false;
 1002 }
 1003 
 1004 double
 1005 SX11Font::width (const SString& xlfd)
 1006 {
 1007   SX11FontCache* fc = loadFont(impl, xlfd);
 1008   if (!fc->load()) return 0.0;
 1009   return fc->fontWidth;
 1010 }
 1011 
 1012 double
 1013 SX11Font::ascent (const SString& xlfd)
 1014 {
 1015   SX11FontCache* fc = loadFont(impl, xlfd);
 1016   if (!fc->load()) return 0.0;
 1017   return fc->fontAscent;
 1018 }
 1019 
 1020 double
 1021 SX11Font::descent (const SString& xlfd)
 1022 {
 1023   SX11FontCache* fc = loadFont(impl, xlfd);
 1024   if (!fc->load()) return 0.0;
 1025   return fc->fontDescent;
 1026 }
 1027 
 1028 double
 1029 SX11Font::gap (const SString& xlfd)
 1030 {
 1031   return 0.0;
 1032 }
 1033 
 1034 static SX11FontCache*
 1035 loadFont (SX11Impl* impl, const SString& xlfd)
 1036 {
 1037   SX11FontCache* f = fontCache.get (xlfd);
 1038   if (f!=0) return f;
 1039   f = new SX11FontCache (impl, xlfd);
 1040   CHECK_NEW (f);
 1041   fontCache.put (xlfd, f);
 1042   return f;
 1043 }
 1044 
 1045