Back to index

lightning-sunbird  0.9+nobinonly
nsATSUIUtils.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsATSUIUtils.h"
00039 #include "nsIDeviceContext.h"
00040 #include "nsDrawingSurfaceMac.h"
00041 #include "nsDeviceContextMac.h"
00042 #include "nsTransform2D.h"
00043 #include "plhash.h"
00044 #include "nsFontUtils.h"
00045 
00046 #include <Gestalt.h>
00047 #include <FixMath.h>
00048 
00049 
00050 //------------------------------------------------------------------------
00051 //     ATSUILayoutCache
00052 //
00053 //------------------------------------------------------------------------
00054 
00055 
00056 //--------------------------------
00057 // Class definition
00058 //--------------------------------
00059 class ATSUILayoutCache
00060 {
00061 public:
00062        ATSUILayoutCache();
00063        ~ATSUILayoutCache();
00064 
00065        PRBool Get(short aFont, short aSize, PRBool aBold, PRBool aItalic, nscolor aColor, ATSUTextLayout *aTxlayout);
00066        void   Set(short aFont, short aSize, PRBool aBold, PRBool aItalic, nscolor aColor, ATSUTextLayout aTxlayout);
00067 
00068 private:
00069        typedef struct
00070        {
00071               short  font;
00072               short  size;
00073               nscolor       color;
00074               short  boldItalic;
00075        } atsuiLayoutCacheKey;
00076 
00077        PRBool Get(atsuiLayoutCacheKey *key, ATSUTextLayout *txlayout);
00078        void   Set(atsuiLayoutCacheKey *key, ATSUTextLayout txlayout);
00079 
00080        static PR_CALLBACK PLHashNumber HashKey(const void *aKey);
00081        static PR_CALLBACK PRIntn          CompareKeys(const void *v1, const void *v2);
00082        static PR_CALLBACK PRIntn          CompareValues(const void *v1, const void *v2);
00083        static PR_CALLBACK PRIntn          FreeHashEntries(PLHashEntry *he, PRIntn italic, void *arg);
00084 
00085        struct PLHashTable*         mTable;
00086        PRUint32                           mCount;
00087 };
00088 
00089 
00090 ATSUILayoutCache::ATSUILayoutCache()
00091 {
00092        mTable = PL_NewHashTable(8, (PLHashFunction)HashKey, 
00093                                                         (PLHashComparator)CompareKeys, 
00094                                                         (PLHashComparator)CompareValues,
00095                                                         nsnull, nsnull);
00096        mCount = 0;
00097 }
00098 
00099 
00100 //--------------------------------
00101 // Public methods
00102 //--------------------------------
00103 
00104 ATSUILayoutCache::~ATSUILayoutCache()
00105 {
00106        if (mTable)
00107        {
00108               PL_HashTableEnumerateEntries(mTable, FreeHashEntries, 0);
00109               PL_HashTableDestroy(mTable);
00110               mTable = nsnull;
00111        }
00112 }
00113 
00114 
00115 PRBool ATSUILayoutCache::Get(short aFont, short aSize, PRBool aBold, PRBool aItalic, nscolor aColor, ATSUTextLayout *aTxlayout)
00116 {
00117        atsuiLayoutCacheKey k = {aFont, aSize, aColor, (aBold ? 1 : 0) + (aItalic ? 2 : 0) };
00118        return Get(&k, aTxlayout);
00119 }
00120 
00121 
00122 void ATSUILayoutCache::Set(short aFont, short aSize, PRBool aBold, PRBool aItalic, nscolor aColor, ATSUTextLayout aTxlayout)
00123 {
00124        atsuiLayoutCacheKey k = {aFont, aSize, aColor, (aBold ? 1 : 0) + (aItalic ? 2 : 0) };
00125        return Set(&k, aTxlayout);
00126 }
00127 
00128 
00129 //--------------------------------
00130 // Private methods
00131 //--------------------------------
00132 
00133 PRBool ATSUILayoutCache::Get(atsuiLayoutCacheKey *key, ATSUTextLayout *txlayout)
00134 {
00135        PLHashEntry **hep = PL_HashTableRawLookup(mTable, HashKey(key), key);
00136        PLHashEntry *he = *hep;
00137        if( he )
00138        {
00139               *txlayout = (ATSUTextLayout)he->value;
00140               return PR_TRUE;
00141        }
00142        return PR_FALSE;
00143 }
00144 
00145 
00146 void ATSUILayoutCache::Set(atsuiLayoutCacheKey *key, ATSUTextLayout txlayout)
00147 {
00148        atsuiLayoutCacheKey *newKey = new atsuiLayoutCacheKey;
00149        if (newKey)
00150        {
00151               *newKey = *key;
00152               PL_HashTableAdd(mTable, newKey, txlayout);
00153               mCount ++;
00154        }
00155 }
00156 
00157 
00158 PR_CALLBACK PLHashNumber ATSUILayoutCache::HashKey(const void *aKey)
00159 {
00160        atsuiLayoutCacheKey* key = (atsuiLayoutCacheKey*)aKey;  
00161        return        key->font + (key->size << 7) + (key->boldItalic << 12) + key->color;
00162 }
00163 
00164 
00165 PR_CALLBACK PRIntn ATSUILayoutCache::CompareKeys(const void *v1, const void *v2)
00166 {
00167        atsuiLayoutCacheKey *k1 = (atsuiLayoutCacheKey *)v1;
00168        atsuiLayoutCacheKey *k2 = (atsuiLayoutCacheKey *)v2;
00169        return (k1->font == k2->font) && (k1->color == k2->color ) && (k1->size == k2->size) && (k1->boldItalic == k2->boldItalic);
00170 }
00171 
00172 
00173 PR_CALLBACK PRIntn ATSUILayoutCache::CompareValues(const void *v1, const void *v2)
00174 {
00175        ATSUTextLayout t1 = (ATSUTextLayout)v1;
00176        ATSUTextLayout t2 = (ATSUTextLayout)v2;
00177        return (t1 == t2);
00178 }
00179 
00180 
00181 PR_CALLBACK PRIntn ATSUILayoutCache::FreeHashEntries(PLHashEntry *he, PRIntn italic, void *arg)
00182 {
00183        delete (atsuiLayoutCacheKey*)he->key;
00184        ATSUDisposeTextLayout((ATSUTextLayout)he->value);
00185        return HT_ENUMERATE_REMOVE;
00186 }
00187 
00188 
00189 #pragma mark -
00190 //------------------------------------------------------------------------
00191 //     nsATSUIUtils
00192 //
00193 //------------------------------------------------------------------------
00194 
00195 
00196 //--------------------------------
00197 // globals
00198 //--------------------------------
00199 
00200 ATSUILayoutCache* nsATSUIUtils::gTxLayoutCache = nsnull;
00201 
00202 PRBool nsATSUIUtils::gIsAvailable = PR_FALSE;
00203 PRBool nsATSUIUtils::gInitialized = PR_FALSE;
00204 
00205 
00206 //--------------------------------
00207 // Initialize
00208 //--------------------------------
00209 
00210 void nsATSUIUtils::Initialize()
00211 {
00212   if (!gInitialized)
00213   {
00214     long version;
00215     gIsAvailable = (::Gestalt(gestaltATSUVersion, &version) == noErr);
00216 
00217     gTxLayoutCache = new ATSUILayoutCache();
00218     if (!gTxLayoutCache)
00219       gIsAvailable = PR_FALSE;
00220 
00221     gInitialized = PR_TRUE;
00222   }
00223 }
00224 
00225 
00226 //--------------------------------
00227 // IsAvailable
00228 //--------------------------------
00229 
00230 PRBool nsATSUIUtils::IsAvailable()
00231 {
00232        return gIsAvailable;
00233 }
00234 
00235 
00236 #pragma mark -
00237 
00238 //------------------------------------------------------------------------
00239 //     nsATSUIToolkit
00240 //
00241 //------------------------------------------------------------------------
00242 nsATSUIToolkit::nsATSUIToolkit()
00243 {
00244        nsATSUIUtils::Initialize();
00245 }
00246 
00247 
00248 //------------------------------------------------------------------------
00249 //     GetTextLayout
00250 //
00251 //------------------------------------------------------------------------
00252 
00253 #define FloatToFixed(a)            ((Fixed)((float)(a) * fixed1))
00254 #define ATTR_CNT 5
00255 
00256 //------------------------------------------------------------------------
00257 ATSUTextLayout nsATSUIToolkit::GetTextLayout(short aFontNum, short aSize, PRBool aBold, PRBool aItalic, nscolor aColor)
00258 { 
00259        ATSUTextLayout txLayout = nsnull;
00260        OSStatus err;
00261        if (nsATSUIUtils::gTxLayoutCache->Get(aFontNum, aSize, aBold, aItalic, aColor, &txLayout))
00262               return txLayout;
00263               
00264        UniChar dmy[1];
00265        err = ::ATSUCreateTextLayoutWithTextPtr (dmy, 0,0,0,0,NULL, NULL, &txLayout);
00266        if(noErr != err) {
00267               NS_WARNING("ATSUCreateTextLayoutWithTextPtr failed");
00268     // goto errorDone;
00269     return nsnull;
00270        }
00271 
00272        ATSUStyle                          theStyle;
00273        err = ::ATSUCreateStyle(&theStyle);
00274        if(noErr != err) {
00275               NS_WARNING("ATSUCreateStyle failed");
00276     // goto errorDoneDestroyTextLayout;
00277        err = ::ATSUDisposeTextLayout(txLayout);
00278     return nsnull;
00279        }
00280 
00281        ATSUAttributeTag            theTag[ATTR_CNT];
00282        ByteCount                          theValueSize[ATTR_CNT];
00283        ATSUAttributeValuePtr       theValue[ATTR_CNT];
00284 
00285        //--- Font ID & Face -----         
00286        ATSUFontID atsuFontID;
00287        
00288        // The use of ATSUFONDtoFontID is not recommended, see
00289        // http://developer.apple.com/documentation/Carbon/Reference/ATSUI_Reference/atsu_reference_Reference/chapter_1.2_section_19.html
00290        FMFontStyle fbStyle;
00291        if (::FMGetFontFromFontFamilyInstance(aFontNum, 0, &atsuFontID, &fbStyle) == kFMInvalidFontErr) {
00292               NS_WARNING("FMGetFontFromFontFamilyInstance failed");
00293     // goto errorDoneDestroyStyle;
00294        err = ::ATSUDisposeStyle(theStyle);
00295        err = ::ATSUDisposeTextLayout(txLayout);
00296     return nsnull;
00297        }
00298        
00299        theTag[0] = kATSUFontTag;
00300        theValueSize[0] = (ByteCount) sizeof(ATSUFontID);
00301        theValue[0] = (ATSUAttributeValuePtr) &atsuFontID;
00302        //--- Font ID & Face  -----        
00303        
00304        //--- Size -----            
00305        float  dev2app;
00306        short fontsize = aSize;
00307 
00308        dev2app = mContext->DevUnitsToAppUnits();
00309   //   Fixed size = FloatToFixed( roundf(float(fontsize) / dev2app));
00310   Fixed size = FloatToFixed( (float) rint(float(fontsize) / dev2app));
00311        if( FixRound ( size ) < 9  && !nsFontUtils::DisplayVerySmallFonts())
00312               size = X2Fix(9);
00313 
00314        theTag[1] = kATSUSizeTag;
00315        theValueSize[1] = (ByteCount) sizeof(Fixed);
00316        theValue[1] = (ATSUAttributeValuePtr) &size;
00317        //--- Size -----            
00318        
00319        //--- Color -----           
00320        RGBColor color;
00321 
00322        #define COLOR8TOCOLOR16(color8)     ((color8 << 8) | color8)          
00323 
00324        color.red = COLOR8TOCOLOR16(NS_GET_R(aColor));
00325        color.green = COLOR8TOCOLOR16(NS_GET_G(aColor));
00326        color.blue = COLOR8TOCOLOR16(NS_GET_B(aColor));                       
00327        theTag[2] = kATSUColorTag;
00328        theValueSize[2] = (ByteCount) sizeof(RGBColor);
00329        theValue[2] = (ATSUAttributeValuePtr) &color;
00330        //--- Color -----           
00331 
00332        //--- Bold -----
00333        Boolean isBold = aBold ? true : false;
00334        theTag[3] = kATSUQDBoldfaceTag;
00335        theValueSize[3] = (ByteCount) sizeof(Boolean);
00336        theValue[3] = (ATSUAttributeValuePtr) &isBold;
00337        //--- Bold -----
00338 
00339        //--- Italic -----
00340        Boolean isItalic = aItalic ? true : false;
00341        theTag[4] = kATSUQDItalicTag;
00342        theValueSize[4] = (ByteCount) sizeof(Boolean);
00343        theValue[4] = (ATSUAttributeValuePtr) &isItalic;
00344        //--- Italic -----
00345 
00346        err =  ::ATSUSetAttributes(theStyle, ATTR_CNT, theTag, theValueSize, theValue);
00347        if(noErr != err) {
00348               NS_WARNING("ATSUSetAttributes failed");
00349     // goto errorDoneDestroyStyle;
00350        err = ::ATSUDisposeStyle(theStyle);
00351        err = ::ATSUDisposeTextLayout(txLayout);
00352     return nsnull;
00353        }
00354               
00355        err = ::ATSUSetRunStyle(txLayout, theStyle, kATSUFromTextBeginning, kATSUToTextEnd);
00356        if(noErr != err) {
00357               NS_WARNING("ATSUSetRunStyle failed");
00358     // goto errorDoneDestroyStyle;
00359        err = ::ATSUDisposeStyle(theStyle);
00360        err = ::ATSUDisposeTextLayout(txLayout);
00361     return nsnull;
00362        }
00363        
00364     err = ::ATSUSetTransientFontMatching(txLayout, true);
00365        if(noErr != err) {
00366               NS_WARNING( "ATSUSetTransientFontMatching failed");
00367     // goto errorDoneDestroyStyle;
00368        err = ::ATSUDisposeStyle(theStyle);
00369        err = ::ATSUDisposeTextLayout(txLayout);
00370     return nsnull;
00371        }
00372        
00373        nsATSUIUtils::gTxLayoutCache->Set(aFontNum, aSize, aBold, aItalic, aColor,  txLayout);     
00374 
00375        return txLayout;
00376 }
00377 
00378 //------------------------------------------------------------------------
00379 //     PrepareToDraw
00380 //
00381 //------------------------------------------------------------------------
00382 void nsATSUIToolkit::PrepareToDraw(CGrafPtr aPort, nsIDeviceContext* aContext)
00383 {
00384    mPort = aPort;
00385    mContext = aContext;
00386 }
00387 
00388 //------------------------------------------------------------------------
00389 //     StartDraw
00390 //
00391 //------------------------------------------------------------------------
00392 void nsATSUIToolkit::StartDraw(
00393        const PRUnichar *aCharPt,
00394        PRUint32 aLen,
00395        short aSize, short aFontNum,
00396        PRBool aBold, PRBool aItalic, nscolor aColor, ATSUTextLayout& oLayout)
00397 {
00398   OSStatus err = noErr;
00399   oLayout = GetTextLayout(aFontNum, aSize, aBold, aItalic, aColor);
00400   if (nsnull == oLayout) {
00401     NS_WARNING("GetTextLayout return nsnull");
00402        return;
00403   }
00404 
00405   // ATSUSetTextPointerLocation won't invalidate atsui's internal cache if aCharPt is
00406   // the same address it already has.  therefore, since we are definitely changing the
00407   // text here, we should explicitly invalidate any existing caches
00408   ::ATSUClearLayoutCache(oLayout, kATSUFromTextBeginning);
00409   
00410   err = ::ATSUSetTextPointerLocation( oLayout, (ConstUniCharArrayPtr)aCharPt, 0, aLen, aLen);
00411   if (noErr != err) {
00412     NS_WARNING("ATSUSetTextPointerLocation failed");
00413        oLayout = nsnull;
00414   }
00415   return;
00416 }
00417 
00418 //------------------------------------------------------------------------
00419 //     GetWidth
00420 //
00421 //------------------------------------------------------------------------
00422 nsresult
00423 nsATSUIToolkit::GetTextDimensions(
00424   const PRUnichar *aCharPt,
00425   PRUint32 aLen, 
00426   nsTextDimensions& oDim,
00427   short aSize, short aFontNum,
00428   PRBool aBold, PRBool aItalic, nscolor aColor)
00429 {
00430   if (!nsATSUIUtils::IsAvailable())
00431     return NS_ERROR_NOT_INITIALIZED;
00432 
00433   StPortSetter    setter(mPort);
00434 
00435   ATSUTextLayout aTxtLayout;
00436   StartDraw(aCharPt, aLen, aSize, aFontNum, aBold, aItalic, aColor, aTxtLayout);
00437   if (nsnull == aTxtLayout) 
00438      return NS_ERROR_FAILURE;
00439 
00440   OSStatus err = noErr;
00441   ATSUTextMeasurement after;
00442   ATSUTextMeasurement ascent;
00443   ATSUTextMeasurement descent;
00444   err = ::ATSUGetUnjustifiedBounds(aTxtLayout, 0, aLen, NULL, &after, &ascent,
00445                                    &descent);
00446   if (noErr != err)
00447   {
00448     NS_WARNING("ATSUGetUnjustifiedBounds failed");
00449     return NS_ERROR_FAILURE;
00450   }
00451 
00452   oDim.width = FixRound(after);
00453   oDim.ascent = FixRound(ascent);
00454   oDim.descent = FixRound(descent);
00455   // aTxtLayout is cached and does not need to be disposed
00456   return NS_OK;
00457 }
00458 
00459 #ifdef MOZ_MATHML
00460 //------------------------------------------------------------------------
00461 // GetBoundingMetrics
00462 //
00463 //------------------------------------------------------------------------
00464 nsresult 
00465 nsATSUIToolkit::GetBoundingMetrics(
00466   const PRUnichar *aCharPt, 
00467   PRUint32 aLen, 
00468   nsBoundingMetrics &oBoundingMetrics,
00469   short aSize, short aFontNum,
00470   PRBool aBold, PRBool aItalic, 
00471   nscolor aColor)
00472 {
00473   if(!nsATSUIUtils::IsAvailable())
00474     return NS_ERROR_NOT_INITIALIZED;
00475 
00476   StPortSetter setter(mPort);
00477 
00478   ATSUTextLayout aTxtLayout;
00479   StartDraw(aCharPt, aLen, aSize, aFontNum, aBold, aItalic, aColor, aTxtLayout);
00480   if(nsnull == aTxtLayout)
00481     return NS_ERROR_FAILURE;
00482 
00483   OSStatus err = noErr;
00484   Rect rect;
00485   ATSUTextMeasurement width;
00486 
00487   if((err = ATSUMeasureTextImage(aTxtLayout, 
00488     kATSUFromTextBeginning, kATSUToTextEnd, 0, 0, &rect)) != noErr)
00489   {
00490     NS_WARNING("ATSUMeasureTextImage failed");
00491     return NS_ERROR_FAILURE;
00492   }
00493 
00494   // return the values in points, the caller will convert them into twips
00495   oBoundingMetrics.leftBearing = rect.left;
00496   oBoundingMetrics.rightBearing = rect.right;
00497   oBoundingMetrics.ascent = -rect.top;
00498   oBoundingMetrics.descent = rect.bottom;
00499 
00500   err = ::ATSUGetUnjustifiedBounds(aTxtLayout, kATSUFromTextBeginning,
00501                                    kATSUToTextEnd, NULL, &width, NULL, NULL);
00502   if (err != noErr)
00503   {
00504     oBoundingMetrics.width = oBoundingMetrics.rightBearing;
00505   }
00506   else
00507     oBoundingMetrics.width = FixRound(width);
00508 
00509   return NS_OK;
00510 }
00511 #endif // MOZ_MATHML
00512 
00513 //------------------------------------------------------------------------
00514 //     DrawString
00515 //
00516 //------------------------------------------------------------------------
00517 nsresult
00518 nsATSUIToolkit::DrawString(
00519        const PRUnichar *aCharPt, 
00520        PRUint32 aLen, 
00521        PRInt32 x, PRInt32 y, 
00522        short &oWidth,
00523        short aSize, short aFontNum,
00524        PRBool aBold, PRBool aItalic, nscolor aColor)
00525 {
00526   oWidth = 0;
00527   if (!nsATSUIUtils::IsAvailable())
00528        return NS_ERROR_NOT_INITIALIZED;
00529        
00530   StPortSetter    setter(mPort);
00531 
00532   ATSUTextLayout aTxtLayout;
00533   
00534   StartDraw(aCharPt, aLen, aSize, aFontNum, aBold, aItalic, aColor, aTxtLayout);
00535   if (nsnull == aTxtLayout)
00536     return NS_ERROR_FAILURE;
00537 
00538   OSStatus err = noErr;     
00539   ATSUTextMeasurement iAfter; 
00540   err = ::ATSUGetUnjustifiedBounds(aTxtLayout, 0, aLen, NULL, &iAfter, NULL,
00541                                    NULL);
00542   if (noErr != err) {
00543      NS_WARNING("MeasureText failed");
00544      return NS_ERROR_FAILURE;
00545   } 
00546 
00547   err = ::ATSUDrawText(aTxtLayout, 0, aLen, Long2Fix(x), Long2Fix(y));
00548   if (noErr != err) {
00549     NS_WARNING("ATSUDrawText failed");
00550     return NS_ERROR_FAILURE;
00551   } 
00552 
00553   oWidth = FixRound(iAfter);
00554   // aTxtLayout is cached and does not need to be disposed
00555   return NS_OK;
00556 }