"Fossies" - the Fresh Open Source Software Archive

Member "yudit-3.0.7/swindow/SFont.cpp" (7 Jun 2020, 26786 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 "SFont.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/SFont.h"
   21 
   22 #include "swindow/SFontFB.h"
   23 #include "stoolkit/SMatrix.h"
   24 #include "stoolkit/SHashtable.h"
   25 #include "stoolkit/STextData.h"
   26 #include "stoolkit/SExcept.h"
   27 #include "stoolkit/SCluster.h"
   28 
   29 static SFontFB fallbackFont;
   30 typedef SHashtable<SFontImplVector> SFontHashtable;
   31 static SFontHashtable* fontHashtable=0;
   32 
   33 static SFontFB::SIGN mapFB (const SGlyph& glyph);
   34 
   35 /* write the composing character bellow this */
   36 
   37 /**
   38  * Create a font. The font can have many faces. 
   39  * @param _name is the logical name of this font.
   40  */
   41 SFont::SFont (const SString _name) : name(_name), xlfd (SD_XLFD_ANY)
   42 {
   43  if (fontHashtable == 0 ) fontHashtable = new SFontHashtable();
   44  if (fontHashtable->get (name) != 0) fontVector = (*fontHashtable)[name];
   45  setSize (16.0);
   46 }
   47 /**
   48  * Create a font. The font can have many faces. 
   49  * @param _name is the logical name of this font.
   50  */
   51 SFont::SFont (const SString _name, double _size) : name(_name), xlfd (SD_XLFD_ANY)
   52 {
   53  if (fontHashtable == 0 ) fontHashtable = new SFontHashtable();
   54  if (fontHashtable->get (name) != 0) fontVector = (*fontHashtable)[name];
   55  setSize (_size);
   56 }
   57 
   58 /**
   59  * get a point 16 font called _default
   60  */
   61 SFont::SFont (void) : name("default"), xlfd (SD_XLFD_ANY)
   62 {
   63   static bool _defaultSet=false;
   64   if (_defaultSet==false)
   65   {
   66     SStringVector m(
   67         /* yudit.hex has ohungarian.hex and part of unifont.hex */
   68         // Indic range is not necessary free. But they are the best
   69         "yudit.hex,arabforms.hex,syriacforms.hex,unifont.hex,"
   70         "markus9x18.bdf,markus18x18ja.bdf,"
   71         "-*-*-medium-r-normal--16-*-*-*-c-*-iso8859-1,"
   72         "-*-*-*-*-*--16-*-*-*-c-*-iso8859-1,"
   73         "THOOLIUC.TTF:mlym,"
   74         "MuktiNarrow.ttf:beng,"
   75         "ani.ttf:beng,"
   76         "pothana2000.ttf:telu,"
   77         "TCRCYoutsoUnicode.ttf:tibt,"
   78         "raghu.ttf:deva,"
   79         "mangal.ttf:deva,tunga.ttf:knda,code2000.ttf:taml,"
   80         "raavi.ttf:guru,shruti.ttf:gujr,"
   81         "arialuni.ttf,cyberbit.ttf,"
   82         "code2000.ttf,code2001.ttf:unicode:RL,arial.ttf,"
   83         //"rovasSMP.ttf," // Hosszu Gabor
   84         //"oldhunSMP.ttf," // Michael Everson
   85         "OldHungarian.ttf,"
   86         "OldHungarian_Full.ttf,"
   87         "yudit.ttf,"
   88         "EmojiOneColor.otf:emoji,"
   89         "TwitterColorEmoji-SVGinOT.ttf:emoji"
   90     );
   91     SFontImplVector list;
   92     for (unsigned int i=0; i<m.size(); i++)
   93     {
   94       /* encoding is optional */
   95       SStringVector v(m[i], ":", false);
   96       SString enc = v[0];
   97 
   98       if (v.size()>1 && v[1].size()!=0) enc = v[1];
   99 
  100       SFontImpl impl (v[0], enc);
  101       if (v.size()>2 && v[2].size()>0)
  102       {
  103          SStringVector pvect(v[2], ";");
  104          SProperties props;
  105          for (unsigned int j=0; j<pvect.size(); j++)
  106          {
  107             SStringVector vv(pvect[j], "=", true);
  108             if (vv.size() > 1)
  109             {
  110                props.put (vv[0], vv[1]);
  111             }
  112             else
  113             {
  114                props.put (vv[0], "true");
  115             }
  116          }
  117          impl.setAttributes(props);
  118       }
  119       list.append (impl);
  120     }
  121     put ("default", list);
  122   }
  123   if (fontHashtable == 0 ) fontHashtable = new SFontHashtable();
  124   fontVector = (*fontHashtable)["default"];
  125   setSize (16.0);
  126   
  127 }
  128 
  129 /**
  130  * Copy this font.
  131  */
  132 SFont::SFont (const SFont& font)
  133 {
  134   name = font.name;
  135   xlfd = font.xlfd;
  136   fontAscent = font.fontAscent;
  137   fontDescent = font.fontDescent;
  138   fontWidth = font.fontWidth;
  139   fontGap = font.fontGap;
  140   fontVector = font.fontVector;
  141   fontScale = font.fontScale;
  142   fallbackScale = font.fallbackScale;
  143 }
  144 
  145 /**
  146  * assign this font.
  147  */
  148 SFont
  149 SFont::operator = (const SFont& font)
  150 {
  151   if (&font == this) return *this;
  152   name = font.name;
  153   fontAscent = font.fontAscent;
  154   fontDescent = font.fontDescent;
  155   fontWidth = font.fontWidth;
  156   fontGap = font.fontGap;
  157   xlfd = font.xlfd;
  158   fontScale = font.fontScale;
  159   fallbackScale = font.fallbackScale;
  160   fontVector = font.fontVector;
  161   return *this;
  162 }
  163 
  164 SFont::~SFont()
  165 {
  166 }
  167 
  168 
  169 /**
  170  * Go through the list and set the size.
  171  */
  172 void
  173 SFont::setSize(double points)
  174 {
  175   fontScale = points;
  176   fallbackScale = points;
  177   SS_Matrix2D m;
  178   double sc = fallbackFont.scale();
  179   m.scale (fontScale * sc, fontScale * sc);
  180   fontGap = 0.0;
  181   /* at least */
  182   fontAscent = -1.0;
  183   fontDescent = 0.0;
  184 
  185   for (unsigned int i=0; i<fontVector.size(); i++)
  186   {
  187     SFontImpl im = fontVector[i];
  188     im.scale (fontScale, fontScale);
  189     //fontVector.remove (i);
  190     fontVector.replace (i, im);
  191     double g = im.gap ();
  192     double a = im.ascent ();
  193     double d = im.descent ();
  194     if (g > fontGap) fontGap = g;
  195     if (a > fontAscent) fontAscent = a;
  196     if (d > fontDescent) fontDescent = d;
  197   }
  198   if (fontAscent <= 1.0 || fontAscent < points/2)
  199   {
  200     fontGap =  fallbackFont.gap(m);
  201     fontAscent =  fallbackFont.ascent(m);
  202     fontDescent =  fallbackFont.descent(m);
  203     if (fontAscent < 1.0) fontAscent = 1.0;
  204   }
  205   fallbackScale = fontAscent+fontDescent;
  206 }
  207 
  208 /**
  209  * Get the size of the font.
  210  * @return the size in points
  211  */
  212 double
  213 SFont::getSize () const
  214 {
  215   return fontScale;
  216 }
  217 
  218 /**
  219  * A static method to build a font list.
  220  * This is only use at initialization time.
  221  * @param name is the name of the font.
  222  * @param gface is the face to add to the name.
  223  */
  224 void
  225 SFont::put (const SString name, const SFontImplVector& face)
  226 {
  227   if (fontHashtable == 0 ) fontHashtable = new SFontHashtable();
  228   fontHashtable->put (name, face);
  229 }
  230 
  231 /**
  232  * clear all the stuff in the list
  233  */
  234 void
  235 SFont::clear()
  236 {
  237 }
  238 
  239 /**
  240  * map a fallback font sign.
  241  */
  242 static SFontFB::SIGN
  243 mapFB (const SGlyph& glyph)
  244 {
  245   if (glyph.decompSize() > 1)
  246   {
  247     if (glyph[0] == SD_CD_CR || glyph[1] == SD_CD_LF) return SFontFB::CRLF;
  248     if (glyph[1] == SD_CD_CR || glyph[0] == SD_CD_LF) return SFontFB::LFCR;
  249   }
  250   else
  251   {
  252     if (glyph.getChar() == SD_CD_CR) return SFontFB::CR;
  253     if (glyph.getChar() == SD_CD_LF) return SFontFB::LF;
  254 
  255     if (glyph.getChar() == SD_CD_LS) return SFontFB::LS;
  256     if (glyph.getChar() == SD_CD_FF) return SFontFB::FF;
  257 
  258     if (glyph.getChar() == SD_CD_PS) return SFontFB::PS;
  259     if (glyph.getChar() == SD_CD_TAB) return SFontFB::TAB;
  260     if (glyph.getChar() == SD_CD_LRM) return SFontFB::LRM;
  261     if (glyph.getChar() == SD_CD_RLM) return SFontFB::RLM;
  262     if (glyph.getChar() == SD_CD_ZWJ) return SFontFB::FB_ZWJ;
  263     if (glyph.getChar() == SD_CD_ZWNJ) return SFontFB::FB_ZWNJ;
  264   }
  265   return SFontFB::CTRL;
  266 }
  267 
  268 /**
  269  * Try to draw one single glyph.
  270  * @param canvas is the canvas to draw to 
  271  * @param m is the conversion matrix
  272  * @param uch is the array containing ucs4 
  273  * @prama len is the length of the array
  274  */
  275 void
  276 SFont::draw (SCanvas* canvas, const SPen& pen, const SS_Matrix2D& m, 
  277   const SGlyph& glyph)
  278 {
  279   double currw = 0.0;
  280   bool isSelected = glyph.selected;
  281   if (glyph.isSpecial())
  282   {
  283     SS_Matrix2D sd;
  284     double sc = fallbackFont.scale();
  285     sd.scale (fallbackScale * sc, fallbackScale * sc);
  286     sd.translate (0, -fontDescent);
  287     SS_Matrix2D sm =  m * sd;
  288     SFontFB::SIGN sig = mapFB (glyph);
  289     /* The markers are not mirrored. */
  290     if (!glyph.isLR() && sig != SFontFB::LRM && sig != SFontFB::RLM)
  291     {
  292       currw =  fallbackFont.signWidth (sm, sig);
  293       /* mirroring */
  294       sm.x0 = -sm.x0;
  295       sm.t0 = sm.t0 + currw;
  296     }
  297     fallbackFont.signDraw(canvas, pen, sm, sig, glyph.getFirstChar());
  298     return;
  299   }
  300 
  301 
  302   /* try to draw it with all fonts. */
  303   SS_UCS4 comp =  glyph.getShapedChar();
  304   unsigned int scriptcode = getLigatureScriptCode (comp);
  305   SV_UCS4 ligclust;
  306   if (scriptcode == SD_COMBINING_LIGATURE)
  307   {
  308     unsigned int lgsize  = getLigatureCluster (comp, 0);
  309     if (lgsize > 1)
  310     {
  311       SS_UCS4* lc = new SS_UCS4[lgsize];
  312       CHECK_NEW (lc);
  313       getLigatureCluster (comp, lc);
  314       comp = lc[0];
  315       for (unsigned int i=0; i<lgsize; i++)
  316       {
  317         ligclust.append (lc[i]);
  318       }
  319       delete[] lc;
  320     }
  321   }
  322   else if (!glyph.isLR() && glyph.isMirrorable())
  323   {
  324     comp = glyph.getMirroredChar();
  325   }
  326 
  327   /* gsize is the full size */
  328   unsigned int gsize = glyph.decompSize();
  329   /* This array is the full array */
  330   const SS_UCS4* decomp = glyph.getDecompArray(); 
  331 
  332   if (comp == 0 && glyph.decompSize() == 1) comp = decomp[0];
  333 
  334   /* first try the precomposed */
  335   bool baseOK = false;
  336   double baseWidth = 0.0;
  337   setBase (comp);
  338   if (comp != 0 && comp!= 0x200c && comp != 0x200d)
  339   {
  340     unsigned int i;
  341 
  342     /* use mirrored glyphs for Old Hungarian, Old Italic */
  343     if (  (comp >= 0xEE00 && comp < 0xEE9F)
  344         || (comp >= 0x10300 && comp <= 0x1032F)
  345         || (comp >= 0x10c00 && comp <= 0x10fff)
  346         || scriptcode == SD_ROVASIRAS || scriptcode == SD_PUA_ROVAS)
  347     {
  348       /* Try to use lr and rl attributes. This is the non-mirrored dwaring. */
  349       for (i=0; i<fontVector.size(); i++)
  350       {
  351         SFontImpl im = fontVector[i];
  352         // Skip cases where font is strongly the opposite direction.
  353         if (im.needSoftMirror (comp, glyph.isLR())) continue;
  354         
  355         /* this is for better positioning of diacritical marks. */
  356         if (im.draw (canvas, pen, m, comp, glyph.isLR(), isSelected, baseOK)) 
  357         {
  358           im.width (comp, &baseWidth);
  359           baseOK=true;
  360           break;
  361         }
  362       }
  363       SS_Matrix2D mm =  m;
  364       /* Try to mirror it - we drew neutrals and same directions */
  365       if (!baseOK) for (i=0; i<fontVector.size(); i++)
  366       {
  367         SFontImpl im = fontVector[i];
  368         bool used = im.width (comp, &currw);
  369         if (used)
  370         {
  371           /* Mirrorring can be done only if we render on our own. */
  372           mm.x0 = -m.x0;  /* try to indicate mirroring */
  373           if (im.isTTF())
  374           {
  375             mm.t0 = m.t0 + currw;
  376           }
  377           im.draw (canvas, pen, mm, comp, glyph.isLR(), isSelected, baseOK); 
  378           baseWidth = currw;
  379           baseOK = true;
  380           break;
  381         }
  382         currw = 0.0;
  383       }
  384       // Try ligatures composed with ZWJ
  385       // commented out as this is done in im for OldHUngarian and OldItalic
  386 #if 0
  387       if (false && !baseOK && comp > 0x7fffffff) {
  388          unsigned int liglen = getLigatureUnicode(comp, 0);
  389          if (liglen > 0) {
  390             SS_UCS4* chars =  new SS_UCS4[liglen];
  391             CHECK_NEW (chars);
  392             getLigatureUnicode (comp, chars);
  393             unsigned int j;
  394             for (i=0; i<fontVector.size(); i++) {
  395                 SFontImpl im = fontVector[i];
  396                 for (j=0; j<liglen; j++) {
  397                     if (chars[j] == SD_CD_ZWJ) {
  398                         if (j%2 != 1) break;
  399                         continue;
  400                     }
  401                     if (j%2 != 0) break;
  402                     if (!im.width (chars[j], &currw)) {
  403                         break;
  404                     }
  405                 }
  406                 if (j < liglen) continue;
  407                 baseWidth = 0;
  408                 for (j=0; j<liglen; j++) {
  409                     SS_UCS4 ch = glyph.isLR() ? chars[j] 
  410                         : chars[liglen-j-1];
  411                     if (ch == SD_CD_ZWJ) continue;
  412                     unsigned int progress = 0;
  413                     im.width (ch, &currw);
  414                     baseWidth += currw;
  415                     SS_Matrix2D mm =  m;
  416                     if (im.needSoftMirror (ch, glyph.isLR())) {
  417                         mm.x0 = -m.x0;  // try to indicate mirroring
  418                         if (im.isTTF())
  419                         {
  420                             mm.t0 = m.t0 + currw;
  421                         }
  422                     }
  423                     mm.t0 = mm.t0 + progress;
  424              //       im.draw (canvas, pen, mm, ch, glyph.isLR()); 
  425                     progress += currw;
  426                 }
  427                 baseOK = true;
  428             }
  429             delete [] chars;
  430          }
  431       }
  432 #endif
  433     }
  434     else
  435     {
  436        /* Try all fonts on it */
  437       for (i=0; i<fontVector.size(); i++)
  438       {
  439         SFontImpl im = fontVector[i];
  440         if (im.draw (canvas, pen, m, comp, glyph.isLR(), isSelected, baseOK)) 
  441         {
  442           im.width (comp, &baseWidth);
  443           baseOK = true;
  444           break;
  445         }
  446       }
  447     }
  448   }
  449 
  450   /*
  451    * If it is shaped and current shape is isolated fallback 
  452    */
  453   if (!baseOK && glyph.getShapeArray()!=0 && glyph.currentShape == 0)// ISOLATED
  454   {
  455     SS_UCS4 orig =  glyph.getChar();
  456     if (orig!=comp && orig != 0)
  457     {
  458       setBase (orig);
  459       for (unsigned int i=0; i<fontVector.size(); i++)
  460       {
  461         SFontImpl im = fontVector[i];
  462         if (im.draw (canvas, pen, m, orig, glyph.isLR(), isSelected, baseOK)) 
  463         {
  464           im.width (orig, &baseWidth);
  465           baseOK = true;
  466           break;
  467         }
  468       }
  469     }
  470   }
  471 
  472   /* Try precompositions  */
  473   const SS_UCS4* fbs = 0;
  474   if (!baseOK && gsize > 0)
  475   {
  476     double* positions = new double[gsize];
  477     unsigned int* indeces = new unsigned int[gsize];
  478     CHECK_NEW (positions);
  479     CHECK_NEW (indeces);
  480     bool found = false;
  481 
  482     bool overstrike =  (glyph.getShapeArray()==0 
  483         && !glyph.isYuditLigature() && !glyph.isCluster());
  484 
  485 
  486     /*
  487      * Hack for special Yudit ligatures 
  488      * Normally clusters are not OVERSTRIKE.
  489      * MARK Composing Cluster: see bin/cluster/cluster.template 
  490     */
  491     if (gsize > 1 && 
  492         (decomp[1] == 0x309A || decomp[1] == 0x300 || decomp[1] == 0x301))
  493     {
  494       overstrike = true;
  495     }
  496     else
  497     {
  498     }
  499     int scode = getLigatureScriptCode (comp); 
  500     if (scode == SD_HANGUL_JAMO 
  501         || scode == SD_LAO || scode == SD_THAI  || scode == SD_TIBETAN)
  502     {
  503       overstrike = true;
  504     }
  505 
  506     /* shape fallback does not include composing marks */
  507     fbs = glyph.getShapeFallback();
  508     if (fbs)
  509     {
  510       overstrike = false; /* well, this is overstrike sometimes */
  511       decomp = fbs; /* even if we can not draw it, this will be displayed */
  512     }
  513     /* we can not do overstrike positioning on a shape fallback */
  514     if (overstrike)
  515     {
  516       setBase (decomp[0]);
  517     }
  518     else
  519     {
  520       setBase (0);
  521     }
  522 
  523     /* build positions */
  524     unsigned int index = 0;
  525     unsigned int i=0;
  526     unsigned int fsize = fontVector.size();
  527     double fullsize = 0;
  528     while (i<fsize) 
  529     {
  530       SFontImpl im = fontVector[i];
  531       currw = 0.0;
  532       bool used = (index > 0 && decomp[index]==0x200d) 
  533           ? true: im.width (decomp[index], &currw);
  534       /* ZWJ and ZWNJ - use fallback if not present.*/
  535       if (!used && i+1 == fsize && !overstrike)
  536       {
  537          SS_Matrix2D sm;
  538          double sc = fallbackFont.scale();
  539          sm.scale (fallbackScale * sc, fallbackScale * sc);
  540          used = true;
  541          i = fsize;
  542          currw = fallbackFont.width (sm, decomp[index]);
  543       }
  544       /* True Type fonts will need to position 
  545          non spacing marks *after* moving cursor */
  546       if (index ==0)
  547       {
  548          if (!used || currw==0)
  549          {
  550            i++; continue;
  551          }
  552          positions[index] = currw; 
  553          fullsize = currw;
  554       }
  555       else if (overstrike) /* may have zero width */
  556       {
  557          if (!used)
  558          {
  559            i++; continue;
  560          }
  561          /* by default don't move caret */
  562          positions[index] = 0;
  563 
  564          /* 
  565           * Should be in sync with: SFontTTF::getBaseOffsets
  566           */
  567          if (!im.isLeftAligned(decomp[index]))
  568          {
  569             positions[index] = positions[0]-currw;
  570          }
  571       }
  572       else
  573       {
  574          /* clusters can have 0 width stuff. */
  575          if (!used)
  576          {
  577            i++; continue;
  578          }
  579          fullsize += currw;
  580          positions[index] = fullsize;
  581       }
  582       indeces[index] = i;
  583       i=0;
  584       index++;
  585       /* found if all found */
  586       if (index == gsize)
  587       {
  588         baseWidth = fullsize;
  589         found = true;
  590         break;
  591       }
  592     }
  593     if (found)
  594     {
  595       for (i=0; i<gsize; i++)
  596       {
  597         SS_Matrix2D mo = m;
  598         double translatex = 0.0;
  599         if (glyph.isLR())
  600         {
  601           if (overstrike)
  602           {
  603              translatex = (i==0) ? 0.0 : positions[i];
  604           }
  605           else
  606           {
  607              translatex = (i==0) ? 0.0 : positions[i-1];
  608           }
  609         }
  610         else
  611         {
  612           if (overstrike)
  613           {
  614              translatex = (i==0) ? 0.0 : positions[i];
  615           }
  616           else
  617           {
  618              translatex = fullsize - positions[i];
  619           }
  620         }
  621         if (indeces[i] == fsize)
  622         {
  623           SS_Matrix2D sd;
  624           double sc = fallbackFont.scale();
  625           sd.scale (fallbackScale * sc, fallbackScale * sc);
  626           sd.translate (0, -fontDescent);
  627           SS_Matrix2D sm =  m * sd;
  628           sm.translate (translatex, (double)0.0);
  629           fallbackFont.draw(canvas, pen, sm, decomp[i]);
  630         }
  631         else
  632         {
  633           mo.translate (translatex, (double)0.0);
  634           SFontImpl im = fontVector[indeces[i]];
  635           im.draw (canvas, pen, mo, decomp[i], glyph.isLR(), isSelected, baseOK);
  636             // SGC
  637            baseOK = true; 
  638         }
  639       }
  640     }
  641     delete[] positions;
  642     delete[] indeces;
  643     if (found) 
  644     {
  645       baseOK = true;
  646       /* combining marks are not part of fallback */
  647       if (fbs==0) ligclust.clear();
  648     }
  649   }
  650 
  651   /*
  652    * If it is shaped and current shape is any fallback 
  653    * Isolated fallback has been processed already.
  654    */
  655   if (!baseOK && glyph.getShapeArray()!=0 && glyph.currentShape != 0) //ISOLATED
  656   {
  657     SS_UCS4 orig =  glyph.getChar();
  658     if (orig!=comp && orig != 0)
  659     {
  660       setBase (orig);
  661       for (unsigned int i=0; i<fontVector.size(); i++)
  662       {
  663         SFontImpl im = fontVector[i];
  664         if (im.draw (canvas, pen, m, orig, glyph.isLR(), isSelected, baseOK)) 
  665         {
  666           im.width (orig, &baseWidth);
  667           baseOK = true;
  668           break;
  669         }
  670       }
  671     }
  672   }
  673 
  674   /* Add extra composing characters as overstrike - if possible  */
  675   if (baseOK) /* we already have set the base with setbase */
  676   {
  677     gsize = glyph.compSize();
  678     decomp = glyph.getCompArray();
  679     /* add composing marks for composing clusters */
  680     unsigned int postcomp = 0;
  681     if (ligclust.size() > 1)
  682     {
  683       ligclust.remove (0);
  684       postcomp = ligclust.size();
  685       for (unsigned int i=0; i<gsize; i++)
  686       {
  687         ligclust.append (decomp[i]);
  688       } 
  689       gsize = ligclust.size();
  690       decomp = ligclust.array();
  691     }
  692     for (unsigned int i=0; i<gsize; i++)
  693     {
  694       /* Try all fonts on it */
  695       SS_Matrix2D mc = m;
  696       for (unsigned int j=0; j<fontVector.size(); j++)
  697       {
  698         SFontImpl im = fontVector[j];
  699         bool used = im.width (decomp[i], &currw);
  700         if (!used) continue;
  701         /* FIXME: find a better way */
  702         /* get these rl scripts right somehow, fonts suppose you go
  703           visual order...  */
  704 
  705         /* Measure LR: composing from the end. */
  706         if (!im.isLeftAligned (decomp[i]))
  707         {
  708           mc.t0 = m.t0 + baseWidth - currw;
  709         }
  710         /* try to play with composing marks in the middle of ligature */
  711         /* This trick works only if ligature has two base characters */
  712         else if (i<postcomp && (im.isTTF() || fbs != 0))
  713         {
  714           /* RL: move it more to the end */
  715           if ((decomp[i] >= 0x500 && decomp[i] < 0x900))
  716           {
  717             mc.t0 = m.t0 + baseWidth / 2;
  718           }
  719         }
  720         /* Draw it finally... */
  721         if (im.draw (canvas, pen, mc, decomp[i], glyph.isLR(), isSelected, baseOK)) 
  722         {
  723            break;
  724         }
  725       }
  726     }
  727   
  728     /* return anyway. 
  729        Extra composing failures will not be indicated for now. There is one 
  730        for sure when you apply the composing to a cluster  */
  731     return;
  732   }
  733 
  734   /* Draw some last resort font. */
  735   SS_Matrix2D sd;
  736   double sc = fallbackFont.scale();
  737   sd.scale (fallbackScale * sc, fallbackScale * sc);
  738   sd.translate (0, -fontDescent);
  739   SS_Matrix2D sm =  m * sd;
  740   SS_Matrix2D mo = sm;
  741   SV_UCS4 allchar;
  742   if (comp != 0)
  743   {
  744     allchar.append (comp);
  745     gsize = glyph.compSize();
  746     decomp = glyph.getCompArray();
  747   }
  748   else
  749   {
  750     /* draw everything. CompArray is right after DecompArray */
  751     gsize += glyph.compSize();
  752   }
  753   for (unsigned int k=0; k<gsize; k++)
  754   {
  755     allchar.append (decomp[k]);
  756   }
  757   gsize = allchar.size();
  758   decomp = allchar.array();
  759 
  760   int myindex = 0;
  761   int inc = 1;
  762   int limit = gsize;
  763   if (!glyph.isLR())
  764   {
  765     myindex = gsize-1;
  766     inc = -1;
  767     limit = -1;
  768   }
  769   while (myindex!=limit)
  770   {
  771     SS_UCS4 chr = decomp[myindex];
  772     if (getLigatureScriptCode (chr) == SD_AS_LITERAL)
  773     {
  774       chr = chr & 0xff;
  775     }
  776     fallbackFont.draw(canvas, pen, mo, chr);
  777     currw = fallbackFont.width (mo, chr);
  778     mo.translate (currw, (double)0.0);
  779     myindex = myindex + inc;
  780   }
  781   return;
  782 }
  783 
  784 
  785 /**
  786  * return the width of the characters
  787  * @param m is the conversion matrix
  788  * @param uch is the array containing ucs4 
  789  * @prama len is the length of the array
  790  */
  791  
  792 double
  793 SFont::width (const SGlyph& glyph)
  794 {
  795   double maxw = 0.0;
  796   double currw = 0.0;
  797   if (glyph.isSpecial ())
  798   {
  799     SS_Matrix2D m;
  800     double sc = fallbackFont.scale();
  801     m.scale (fallbackScale * sc, fallbackScale * sc);
  802     SFontFB::SIGN sig = mapFB (glyph);
  803     currw =  fallbackFont.signWidth (m, sig);
  804     return currw;
  805   }
  806 
  807   /* try to draw it with all fonts. */
  808   SS_UCS4 comp =  glyph.getShapedChar();
  809   unsigned int scriptcode = getLigatureScriptCode (comp);
  810   if (scriptcode == SD_COMBINING_LIGATURE)
  811   {
  812     unsigned int lgsize = getLigatureCluster (comp, 0);
  813     if (lgsize > 1)
  814     {
  815       SS_UCS4* lc = new SS_UCS4[lgsize];
  816       CHECK_NEW (lc);
  817       getLigatureCluster (comp, lc);
  818       comp = lc[0];
  819       delete[] lc;
  820     }
  821   }
  822   else if (!glyph.isLR() && glyph.isMirrorable())
  823   {
  824     comp = glyph.getMirroredChar();
  825   }
  826 
  827   /* first try the precomposed */
  828   if (comp != 0 && comp!= 0x200c && comp != 0x200d)
  829   {
  830     for (unsigned int i=0; i<fontVector.size(); i++)
  831     {
  832       SFontImpl im = fontVector[i];
  833       if (im.width (comp, &currw))
  834       {
  835         if (currw < 0.0) return 1.0;
  836         return currw;
  837       }
  838     }
  839   }
  840   /*
  841    * If it is shaped and current shape is isolated fallback 
  842    */
  843   if (glyph.getShapeArray()!=0 && glyph.currentShape == 0) // ISOLATED
  844   {
  845     SS_UCS4 orig =  glyph.getChar();
  846     if (orig!=comp && orig != 0)
  847     {
  848       for (unsigned int i=0; i<fontVector.size(); i++)
  849       {
  850         SFontImpl im = fontVector[i];
  851         bool used = im.width (orig, &currw);
  852         if (used && currw >0.0)
  853         {
  854           return currw;
  855         }
  856       }
  857     }
  858   }
  859 
  860   /* You reach this point if comp did not work */
  861   unsigned int gsize = glyph.decompSize();
  862   const SS_UCS4* decomp = glyph.getDecompArray(); 
  863 
  864   if (gsize > 0)
  865   {
  866     bool overstrike =  (glyph.getShapeArray() ==0 
  867         && !glyph.isYuditLigature() && !glyph.isCluster());
  868 
  869     /* hack for special Yudit ligatures */
  870     /* MARK Composing Cluster: see bin/cluster/cluster.template */
  871     if (gsize > 1 && 
  872         (decomp[1] == 0x309A || decomp[1] == 0x300 || decomp[1] == 0x301))
  873     {
  874       overstrike = true;
  875     }
  876     int scode = getLigatureScriptCode (comp); 
  877     if (scode == SD_HANGUL_JAMO 
  878         || scode == SD_LAO || scode == SD_THAI  || scode == SD_TIBETAN)
  879     {
  880       overstrike = true;
  881     }
  882 
  883     const SS_UCS4* fbs = glyph.getShapeFallback();
  884     if (fbs)
  885     {
  886       overstrike = false;
  887       decomp = fbs; /* even if we can not draw it, this will be displayed */
  888     }
  889 
  890     unsigned int index = 0;
  891     unsigned int i=0;
  892     unsigned int fsize = fontVector.size();
  893     while (i<fsize)
  894     {
  895       SFontImpl im = fontVector[i];
  896       currw = 0.0; 
  897       bool used = (index > 0 && decomp[index]==0x200d)
  898         ? true : im.width (decomp[index], &currw);
  899       /* can draw fallback in the middle */
  900       if (!used && i+1 == fsize && !overstrike)
  901       {
  902          SS_Matrix2D sm;
  903          double sc = fallbackFont.scale();
  904          sm.scale (fallbackScale * sc, fallbackScale * sc);
  905          used = true;
  906          i = fsize;
  907          currw = fallbackFont.width (sm, decomp[index]);
  908       }
  909       if (index==0 && (currw==0.0 || !used))
  910       {
  911           i++; continue;
  912       }
  913        /* clusters also can have 0 width stuff */
  914       if (!used)
  915       {
  916         i++; continue;
  917       }
  918       if (overstrike)
  919       {
  920         if (index==0) maxw = currw;
  921       }
  922       else
  923       {
  924         maxw += currw;
  925       }
  926       index++;
  927       i = 0;
  928       if (index >= gsize)
  929       {
  930         if (maxw > 0.0) return maxw;
  931         break;
  932       }
  933     }
  934   }
  935   /*
  936    * If it is shaped and current shape is any fallback 
  937    * Isolated fallback has been processed already.
  938    */
  939   if (glyph.getShapeArray()!=0 && glyph.currentShape != 0) // ISOLATED
  940   {
  941     SS_UCS4 orig =  glyph.getChar();
  942     if (orig!=comp && orig != 0)
  943     {
  944       for (unsigned int i=0; i<fontVector.size(); i++)
  945       {
  946         SFontImpl im = fontVector[i];
  947         bool used = im.width (orig, &currw);
  948         if (used && currw >0.0)
  949         {
  950           return currw;
  951         }
  952       }
  953     }
  954   }
  955 
  956   /* last resort font */
  957   SS_Matrix2D sm;
  958   double sc = fallbackFont.scale();
  959   sm.scale (fallbackScale * sc, fallbackScale * sc);
  960   maxw  = 0;
  961   if (comp != 0)
  962   {
  963     SS_UCS4 chr = comp;
  964     if (getLigatureScriptCode (chr) == SD_AS_LITERAL)
  965     {
  966       chr = chr & 0xff;
  967     }
  968     maxw = fallbackFont.width (sm, chr);
  969     gsize = glyph.compSize();
  970     decomp = glyph.getCompArray();
  971   }
  972   else
  973   {
  974     /* draw everything */
  975     gsize += glyph.compSize();
  976   }
  977   for (unsigned int i=0; i< gsize; i++)
  978   {
  979     SS_UCS4 chr = decomp[i];
  980     if (getLigatureScriptCode (chr) == SD_AS_LITERAL)
  981     {
  982       chr = chr & 0xff;
  983     }
  984     maxw = maxw + fallbackFont.width (sm, chr);
  985   }
  986   return maxw;
  987 }
  988 
  989 /**
  990  * return the overall width
  991  */
  992 double
  993 SFont::width () const
  994 {
  995   return fontWidth;
  996 }
  997 
  998 /**
  999  * return the overall ascent
 1000  */
 1001 double
 1002 SFont::ascent () const
 1003 {
 1004   return fontAscent;
 1005 }
 1006 
 1007 
 1008 /**
 1009  * return the overall descent
 1010  */
 1011 double
 1012 SFont::descent () const
 1013 {
 1014   return fontDescent;
 1015 }
 1016 
 1017 /**
 1018  * return the overall gap
 1019  */
 1020 double
 1021 SFont::gap () const
 1022 {
 1023   return fontGap;
 1024 }